vgapp 1.0.3 → 1.0.4
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.
|
@@ -137,12 +137,12 @@ class VGFilesBase extends BaseModule {
|
|
|
137
137
|
Boolean(this._params?.replace) &&
|
|
138
138
|
isSingle;
|
|
139
139
|
|
|
140
|
-
if (shouldReplaceOnSingle) {
|
|
141
|
-
this.clear(false);
|
|
142
|
-
this.append(filesArray, true);
|
|
143
|
-
this._revokeUrls();
|
|
144
|
-
this._cleanupFakeInputs();
|
|
145
|
-
this._cleanupErrors();
|
|
140
|
+
if (shouldReplaceOnSingle) {
|
|
141
|
+
this.clear(false);
|
|
142
|
+
this.append(filesArray, true);
|
|
143
|
+
this._revokeUrls();
|
|
144
|
+
this._cleanupFakeInputs();
|
|
145
|
+
this._cleanupErrors();
|
|
146
146
|
|
|
147
147
|
this._files = this._filterFiles(filesArray);
|
|
148
148
|
if (this._params.prepend) this._files.reverse();
|
|
@@ -209,6 +209,56 @@ class VGFilesBase extends BaseModule {
|
|
|
209
209
|
return `${file.name}-${file.size}-${file.type}`;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
_getFileCustomData(file) {
|
|
213
|
+
const customData = file?.customData;
|
|
214
|
+
if (!customData || typeof customData !== 'object' || Array.isArray(customData)) return {};
|
|
215
|
+
return customData;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
_toDataAttributeKey(key) {
|
|
219
|
+
if (!key) return '';
|
|
220
|
+
|
|
221
|
+
return String(key)
|
|
222
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
223
|
+
.replace(/[_\s]+/g, '-')
|
|
224
|
+
.replace(/[^a-zA-Z0-9-]/g, '')
|
|
225
|
+
.replace(/-+/g, '-')
|
|
226
|
+
.replace(/^-|-$/g, '')
|
|
227
|
+
.toLowerCase();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
_toDataAttributeValue(value) {
|
|
231
|
+
if (value === undefined || value === null || value === '') return null;
|
|
232
|
+
if (typeof value === 'object') {
|
|
233
|
+
try {
|
|
234
|
+
return JSON.stringify(value);
|
|
235
|
+
} catch (e) {
|
|
236
|
+
return String(value);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return value;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
_buildFileDataAttributes(file, baseAttrs = {}) {
|
|
243
|
+
const attrs = { ...baseAttrs };
|
|
244
|
+
const customData = this._getFileCustomData(file);
|
|
245
|
+
|
|
246
|
+
Object.entries(customData).forEach(([key, value]) => {
|
|
247
|
+
const attrKey = this._toDataAttributeKey(key);
|
|
248
|
+
if (!attrKey) return;
|
|
249
|
+
|
|
250
|
+
const attrName = `data-${attrKey}`;
|
|
251
|
+
if (Object.prototype.hasOwnProperty.call(attrs, attrName)) return;
|
|
252
|
+
|
|
253
|
+
const attrValue = this._toDataAttributeValue(value);
|
|
254
|
+
if (attrValue === null) return;
|
|
255
|
+
|
|
256
|
+
attrs[attrName] = attrValue;
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
return attrs;
|
|
260
|
+
}
|
|
261
|
+
|
|
212
262
|
_filterFiles(files) {
|
|
213
263
|
this._errors.clear();
|
|
214
264
|
const { count, sizes, total } = this._params.limits;
|
|
@@ -378,14 +428,20 @@ class VGFilesBase extends BaseModule {
|
|
|
378
428
|
}
|
|
379
429
|
}
|
|
380
430
|
|
|
381
|
-
let parts = [];
|
|
382
|
-
$itemsTemplate.forEach(tmpl => {
|
|
383
|
-
const part = this._renderTemplatePart(tmpl.element, file, null, { isDrop: true });
|
|
384
|
-
if (part) parts.push(part);
|
|
385
|
-
});
|
|
431
|
+
let parts = [];
|
|
432
|
+
$itemsTemplate.forEach(tmpl => {
|
|
433
|
+
const part = this._renderTemplatePart(tmpl.element, file, null, { isDrop: true });
|
|
434
|
+
if (part) parts.push(part);
|
|
435
|
+
});
|
|
386
436
|
|
|
387
437
|
const $li = this._tpl.li(
|
|
388
|
-
|
|
438
|
+
this._buildFileDataAttributes(file, {
|
|
439
|
+
'data-name': file.name,
|
|
440
|
+
'data-size': file.size ?? 0,
|
|
441
|
+
'data-id': file.id || '',
|
|
442
|
+
class: 'file ' + classes.join(' ')
|
|
443
|
+
}),
|
|
444
|
+
parts
|
|
389
445
|
);
|
|
390
446
|
|
|
391
447
|
fragment.appendChild($li);
|
|
@@ -394,17 +450,17 @@ class VGFilesBase extends BaseModule {
|
|
|
394
450
|
$list.innerHTML = '';
|
|
395
451
|
$list.appendChild(fragment);
|
|
396
452
|
|
|
397
|
-
const $message = Selectors.find(`.${this._getClass('drop-message')}`, this._nodes.drop);
|
|
398
|
-
if ($message) {
|
|
399
|
-
if (files.length) Classes.add($message, 'has-files');
|
|
400
|
-
else Classes.remove($message, 'has-files');
|
|
401
|
-
|
|
402
|
-
const isSingle = Number(this._params?.limits?.count) === 1;
|
|
403
|
-
if (isSingle) {
|
|
404
|
-
if (files.length) Classes.remove($message, 'show');
|
|
405
|
-
else Classes.add($message, 'show');
|
|
406
|
-
}
|
|
407
|
-
}
|
|
453
|
+
const $message = Selectors.find(`.${this._getClass('drop-message')}`, this._nodes.drop);
|
|
454
|
+
if ($message) {
|
|
455
|
+
if (files.length) Classes.add($message, 'has-files');
|
|
456
|
+
else Classes.remove($message, 'has-files');
|
|
457
|
+
|
|
458
|
+
const isSingle = Number(this._params?.limits?.count) === 1;
|
|
459
|
+
if (isSingle) {
|
|
460
|
+
if (files.length) Classes.remove($message, 'show');
|
|
461
|
+
else Classes.add($message, 'show');
|
|
462
|
+
}
|
|
463
|
+
}
|
|
408
464
|
|
|
409
465
|
this._nodes.drop.appendChild($list);
|
|
410
466
|
Classes.add(this._nodes.drop, 'active');
|
|
@@ -435,14 +491,21 @@ class VGFilesBase extends BaseModule {
|
|
|
435
491
|
if (this._params.detach) classes.push('with-remove')
|
|
436
492
|
if (this._params.sortable.enabled) classes.push('with-sortable');
|
|
437
493
|
|
|
438
|
-
let parts = [];
|
|
439
|
-
$itemsTemplate.forEach(tmpl => {
|
|
440
|
-
const part = this._renderTemplatePart(tmpl.element, file, i);
|
|
441
|
-
if (part) parts.push(part);
|
|
442
|
-
});
|
|
494
|
+
let parts = [];
|
|
495
|
+
$itemsTemplate.forEach(tmpl => {
|
|
496
|
+
const part = this._renderTemplatePart(tmpl.element, file, i);
|
|
497
|
+
if (part) parts.push(part);
|
|
498
|
+
});
|
|
443
499
|
|
|
444
500
|
const $li = this._tpl.li(
|
|
445
|
-
|
|
501
|
+
this._buildFileDataAttributes(file, {
|
|
502
|
+
'data-name': file.name,
|
|
503
|
+
'data-size': file.size ?? 0,
|
|
504
|
+
'data-type': file.type || '',
|
|
505
|
+
'data-id': file.id || '',
|
|
506
|
+
class: 'file ' + classes.join(' ') + ' '
|
|
507
|
+
}),
|
|
508
|
+
parts
|
|
446
509
|
);
|
|
447
510
|
fragment.appendChild($li);
|
|
448
511
|
});
|
|
@@ -451,38 +514,38 @@ class VGFilesBase extends BaseModule {
|
|
|
451
514
|
Classes.add(this._nodes.info, 'show')
|
|
452
515
|
}
|
|
453
516
|
|
|
454
|
-
_renderTemplatePart(element, file, index = null, options = {}) {
|
|
455
|
-
if (!element) return null;
|
|
456
|
-
const { isDrop = false } = options;
|
|
457
|
-
|
|
458
|
-
const classList = element?.classList;
|
|
459
|
-
|
|
460
|
-
if (classList?.contains('file-image')) return this._renderUIImage(file);
|
|
461
|
-
if (classList?.contains('file-info')) {
|
|
462
|
-
if (isDrop) return null;
|
|
463
|
-
return this._renderUIInfo(file, index);
|
|
464
|
-
}
|
|
465
|
-
if (classList?.contains('file-remove')) return this._wrapInFileCustom(this._renderUIDetach(file));
|
|
466
|
-
|
|
467
|
-
const $part = element.cloneNode(true);
|
|
468
|
-
|
|
469
|
-
this._replaceTemplateSlot($part, '.file-image', () => this._renderUIImage(file));
|
|
470
|
-
this._replaceTemplateSlot($part, '.file-info', () => isDrop ? null : this._renderUIInfo(file, index));
|
|
471
|
-
this._replaceTemplateSlot($part, '.file-remove', () => this._renderUIDetach(file));
|
|
472
|
-
|
|
473
|
-
return this._wrapInFileCustom($part);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
_wrapInFileCustom(node) {
|
|
477
|
-
if (!node) return null;
|
|
478
|
-
if (node.classList?.contains('file-custom')) return node;
|
|
479
|
-
|
|
480
|
-
const wrapper = document.createElement('div');
|
|
481
|
-
wrapper.className = 'file-custom';
|
|
482
|
-
wrapper.appendChild(node);
|
|
483
|
-
|
|
484
|
-
return wrapper;
|
|
485
|
-
}
|
|
517
|
+
_renderTemplatePart(element, file, index = null, options = {}) {
|
|
518
|
+
if (!element) return null;
|
|
519
|
+
const { isDrop = false } = options;
|
|
520
|
+
|
|
521
|
+
const classList = element?.classList;
|
|
522
|
+
|
|
523
|
+
if (classList?.contains('file-image')) return this._renderUIImage(file);
|
|
524
|
+
if (classList?.contains('file-info')) {
|
|
525
|
+
if (isDrop) return null;
|
|
526
|
+
return this._renderUIInfo(file, index);
|
|
527
|
+
}
|
|
528
|
+
if (classList?.contains('file-remove')) return this._wrapInFileCustom(this._renderUIDetach(file));
|
|
529
|
+
|
|
530
|
+
const $part = element.cloneNode(true);
|
|
531
|
+
|
|
532
|
+
this._replaceTemplateSlot($part, '.file-image', () => this._renderUIImage(file));
|
|
533
|
+
this._replaceTemplateSlot($part, '.file-info', () => isDrop ? null : this._renderUIInfo(file, index));
|
|
534
|
+
this._replaceTemplateSlot($part, '.file-remove', () => this._renderUIDetach(file));
|
|
535
|
+
|
|
536
|
+
return this._wrapInFileCustom($part);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
_wrapInFileCustom(node) {
|
|
540
|
+
if (!node) return null;
|
|
541
|
+
if (node.classList?.contains('file-custom')) return node;
|
|
542
|
+
|
|
543
|
+
const wrapper = document.createElement('div');
|
|
544
|
+
wrapper.className = 'file-custom';
|
|
545
|
+
wrapper.appendChild(node);
|
|
546
|
+
|
|
547
|
+
return wrapper;
|
|
548
|
+
}
|
|
486
549
|
|
|
487
550
|
_replaceTemplateSlot(container, selector, renderer) {
|
|
488
551
|
const isMatchSelf = container?.matches && container.matches(selector);
|
|
@@ -586,14 +649,14 @@ class VGFilesBase extends BaseModule {
|
|
|
586
649
|
Selectors.findAll('[data-vg-files="generated"]', this._element).forEach(el => el.remove());
|
|
587
650
|
}
|
|
588
651
|
|
|
589
|
-
clear(resetInput = true) {
|
|
590
|
-
this._revokeUrls();
|
|
591
|
-
if (resetInput) {
|
|
592
|
-
this._resetFileInput();
|
|
593
|
-
}
|
|
594
|
-
this._cleanupFakeInputs();
|
|
595
|
-
this._cleanupErrors();
|
|
596
|
-
this._files = [];
|
|
652
|
+
clear(resetInput = true) {
|
|
653
|
+
this._revokeUrls();
|
|
654
|
+
if (resetInput) {
|
|
655
|
+
this._resetFileInput();
|
|
656
|
+
}
|
|
657
|
+
this._cleanupFakeInputs();
|
|
658
|
+
this._cleanupErrors();
|
|
659
|
+
this._files = [];
|
|
597
660
|
|
|
598
661
|
if (this._nodes.info) {
|
|
599
662
|
Classes.remove(this._nodes.info, 'show');
|
|
@@ -604,15 +667,15 @@ class VGFilesBase extends BaseModule {
|
|
|
604
667
|
const $list = Selectors.find(`.${this._getClass('drop-list')}`, this._element);
|
|
605
668
|
if ($list) $list.innerHTML = '';
|
|
606
669
|
|
|
607
|
-
const $message = Selectors.find(`.${this._getClass('drop-message')}`, this._element);
|
|
608
|
-
if ($message) {
|
|
609
|
-
Classes.remove($message, 'has-files');
|
|
610
|
-
|
|
611
|
-
const isSingle = Number(this._params?.limits?.count) === 1;
|
|
612
|
-
if (isSingle) {
|
|
613
|
-
Classes.add($message, 'show');
|
|
614
|
-
}
|
|
615
|
-
}
|
|
670
|
+
const $message = Selectors.find(`.${this._getClass('drop-message')}`, this._element);
|
|
671
|
+
if ($message) {
|
|
672
|
+
Classes.remove($message, 'has-files');
|
|
673
|
+
|
|
674
|
+
const isSingle = Number(this._params?.limits?.count) === 1;
|
|
675
|
+
if (isSingle) {
|
|
676
|
+
Classes.add($message, 'show');
|
|
677
|
+
}
|
|
678
|
+
}
|
|
616
679
|
|
|
617
680
|
Classes.remove(this._nodes.drop, 'active');
|
|
618
681
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isElement, normalizeData } from "../../../utils/js/functions";
|
|
2
2
|
import Params from "../../../utils/js/components/params";
|
|
3
3
|
import Selectors from "../../../utils/js/dom/selectors";
|
|
4
|
-
import {
|
|
4
|
+
import { Manipulator } from "../../../utils/js/dom/manipulator";
|
|
5
5
|
|
|
6
6
|
class VGFilesTemplateRender {
|
|
7
7
|
constructor(vgFilesInstance, element, params = {}) {
|
|
@@ -38,34 +38,105 @@ class VGFilesTemplateRender {
|
|
|
38
38
|
const $items = Array.from($list.children).filter(li => li.tagName === 'LI');
|
|
39
39
|
if ($items.length === 0) return false;
|
|
40
40
|
|
|
41
|
-
// Сохраняем шаблон только один раз
|
|
42
41
|
this._setTemplateInBuffer($items);
|
|
43
|
-
|
|
44
42
|
if (!this.bufferTemplate) return false;
|
|
45
43
|
|
|
46
|
-
// Парсим данные файлов
|
|
47
44
|
this.parsedFiles = $items
|
|
48
45
|
.map(li => {
|
|
49
46
|
const rawData = Manipulator.get(li, 'data-file');
|
|
50
47
|
if (!rawData) return null;
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
const requiredKeys = ['id', 'name', 'size', 'type', 'src'];
|
|
54
|
-
const isValid = requiredKeys.every(key => dataFile.hasOwnProperty(key));
|
|
55
|
-
|
|
56
|
-
return isValid ? dataFile : null;
|
|
49
|
+
return this._parseDataFile(rawData);
|
|
57
50
|
})
|
|
58
|
-
.filter(Boolean);
|
|
51
|
+
.filter(Boolean);
|
|
59
52
|
|
|
60
53
|
return true;
|
|
61
54
|
}
|
|
62
55
|
|
|
56
|
+
_parseDataFile(rawData) {
|
|
57
|
+
const dataFile = normalizeData(rawData);
|
|
58
|
+
if (!dataFile || typeof dataFile !== 'object' || Array.isArray(dataFile)) return null;
|
|
59
|
+
|
|
60
|
+
const binaryMeta = this._extractBinaryMeta(dataFile);
|
|
61
|
+
const name = this._toStringOrNull(dataFile.name);
|
|
62
|
+
const id = this._toStringOrNull(dataFile.id);
|
|
63
|
+
const src = this._toStringOrNull(dataFile.src);
|
|
64
|
+
const type = this._toStringOrNull(dataFile.type) || binaryMeta.type || 'application/octet-stream';
|
|
65
|
+
const size = this._toNumberOrNull(dataFile.size) ?? binaryMeta.size ?? 0;
|
|
66
|
+
const lastModified = this._toNumberOrNull(dataFile.lastModified ?? dataFile['last-modified']) ?? binaryMeta.lastModified ?? Date.now();
|
|
67
|
+
|
|
68
|
+
const result = {
|
|
69
|
+
id,
|
|
70
|
+
name,
|
|
71
|
+
size,
|
|
72
|
+
type,
|
|
73
|
+
src,
|
|
74
|
+
lastModified
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (dataFile.image !== undefined && dataFile.image !== null && dataFile.image !== '') {
|
|
78
|
+
result.image = dataFile.image;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const requiredKeys = ['id', 'name', 'size', 'type', 'src'];
|
|
82
|
+
const isValid = requiredKeys.every((key) => {
|
|
83
|
+
const value = result[key];
|
|
84
|
+
if (key === 'size') return Number.isFinite(value);
|
|
85
|
+
return value !== undefined && value !== null && value !== '';
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (!isValid) return null;
|
|
89
|
+
|
|
90
|
+
const customData = {};
|
|
91
|
+
const reserved = new Set(['id', 'name', 'size', 'type', 'src', 'image', 'lastModified', 'last-modified']);
|
|
92
|
+
|
|
93
|
+
Object.entries(dataFile).forEach(([key, value]) => {
|
|
94
|
+
if (reserved.has(key)) return;
|
|
95
|
+
if (value === undefined || value === null || value === '') return;
|
|
96
|
+
customData[key] = value;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (Object.keys(customData).length) {
|
|
100
|
+
result.customData = customData;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_extractBinaryMeta(dataFile) {
|
|
107
|
+
const binary = dataFile.file || dataFile.blob || dataFile.originFile || null;
|
|
108
|
+
if (!(binary instanceof Blob)) return {};
|
|
109
|
+
|
|
110
|
+
const size = this._toNumberOrNull(binary.size);
|
|
111
|
+
const type = this._toStringOrNull(binary.type);
|
|
112
|
+
const lastModified = (binary instanceof File)
|
|
113
|
+
? this._toNumberOrNull(binary.lastModified)
|
|
114
|
+
: null;
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
size: size ?? null,
|
|
118
|
+
type: type || null,
|
|
119
|
+
lastModified: lastModified ?? null
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_toStringOrNull(value) {
|
|
124
|
+
if (value === undefined || value === null) return null;
|
|
125
|
+
const normalized = String(value).trim();
|
|
126
|
+
return normalized ? normalized : null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
_toNumberOrNull(value) {
|
|
130
|
+
if (value === undefined || value === null || value === '') return null;
|
|
131
|
+
const normalized = Number(value);
|
|
132
|
+
return Number.isFinite(normalized) ? normalized : null;
|
|
133
|
+
}
|
|
134
|
+
|
|
63
135
|
_setTemplateInBuffer($items) {
|
|
64
136
|
if (this.bufferTemplate || $items.length === 0) return;
|
|
65
137
|
|
|
66
138
|
const firstItem = $items[0];
|
|
67
139
|
|
|
68
|
-
// Если нет data-file — шаблон извлекается и элемент удаляется
|
|
69
140
|
if (!Manipulator.has(firstItem, 'data-file')) {
|
|
70
141
|
this.bufferTemplate = firstItem.outerHTML;
|
|
71
142
|
firstItem.remove();
|
|
@@ -80,4 +151,4 @@ class VGFilesTemplateRender {
|
|
|
80
151
|
}
|
|
81
152
|
}
|
|
82
153
|
|
|
83
|
-
export default VGFilesTemplateRender;
|
|
154
|
+
export default VGFilesTemplateRender;
|
|
@@ -41,12 +41,12 @@ class VGFiles extends VGFilesBase {
|
|
|
41
41
|
types: [],
|
|
42
42
|
ajax: false,
|
|
43
43
|
prepend: true,
|
|
44
|
-
replace: true,
|
|
45
|
-
rename: false,
|
|
46
|
-
smartdrop: false,
|
|
47
|
-
uploads: {
|
|
48
|
-
mode: 'sequential',
|
|
49
|
-
route: '',
|
|
44
|
+
replace: true,
|
|
45
|
+
rename: false,
|
|
46
|
+
smartdrop: false,
|
|
47
|
+
uploads: {
|
|
48
|
+
mode: 'sequential',
|
|
49
|
+
route: '',
|
|
50
50
|
maxParallel: 3,
|
|
51
51
|
maxConcurrent: 1,
|
|
52
52
|
retryAttempts: 1,
|
|
@@ -106,67 +106,91 @@ class VGFiles extends VGFilesBase {
|
|
|
106
106
|
if (this.isRenderNonInit) return;
|
|
107
107
|
this.isRenderNonInit = this._render.init();
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
const parsedFiles = this._render.parsedFiles;
|
|
110
|
+
|
|
111
|
+
if (parsedFiles.length) {
|
|
112
|
+
this._addExternalFiles(parsedFiles);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (this._params.allowed && !this._params.ajax) this._params.detach = false;
|
|
116
|
+
if (this._nodes.drop) {
|
|
117
|
+
this._params.image = true;
|
|
118
|
+
this._params.detach = true;
|
|
119
|
+
this._setDropActiveText();
|
|
120
|
+
|
|
121
|
+
VGFilesDroppable.getOrCreateInstance(this._nodes.drop, this._params).init();
|
|
111
122
|
}
|
|
112
123
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.
|
|
123
|
-
this.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const activeText = (this._nodes.drop.getAttribute('data-drop-active-text') || '').trim() || messages['drop-active'] || 'Release to upload';
|
|
132
|
-
this._nodes.drop.setAttribute('data-drop-active-text', activeText);
|
|
133
|
-
|
|
134
|
-
const title = Selectors.find('.vg-files-drop-message .title', this._nodes.drop);
|
|
135
|
-
if (!title) return;
|
|
136
|
-
|
|
137
|
-
const originalText = (title.getAttribute('data-drop-original-text') || '').trim() || (title.textContent || '').trim();
|
|
138
|
-
title.setAttribute('data-drop-original-text', originalText);
|
|
139
|
-
}
|
|
124
|
+
this._addEventListenerExtended();
|
|
125
|
+
this._renderStat();
|
|
126
|
+
this._triggerCallback('onInit', { element: this._element, files: parsedFiles || [] });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
_setDropActiveText() {
|
|
130
|
+
if (!this._nodes.drop) return;
|
|
131
|
+
|
|
132
|
+
const messages = lang_messages(this._params.lang, NAME) || {};
|
|
133
|
+
const activeText = (this._nodes.drop.getAttribute('data-drop-active-text') || '').trim() || messages['drop-active'] || 'Release to upload';
|
|
134
|
+
this._nodes.drop.setAttribute('data-drop-active-text', activeText);
|
|
135
|
+
|
|
136
|
+
const title = Selectors.find('.vg-files-drop-message .title', this._nodes.drop);
|
|
137
|
+
if (!title) return;
|
|
138
|
+
|
|
139
|
+
const originalText = (title.getAttribute('data-drop-original-text') || '').trim() || (title.textContent || '').trim();
|
|
140
|
+
title.setAttribute('data-drop-original-text', originalText);
|
|
141
|
+
}
|
|
140
142
|
|
|
141
143
|
_addExternalFiles(files) {
|
|
142
144
|
if (!Array.isArray(files) || !files.length) return;
|
|
143
145
|
|
|
144
146
|
files.forEach(fileData => {
|
|
145
|
-
|
|
146
|
-
type: fileData.type || "application/octet-stream",
|
|
147
|
-
lastModified: fileData.lastModified || Date.now()
|
|
148
|
-
});
|
|
147
|
+
if (!fileData?.name) return;
|
|
149
148
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
writable: true,
|
|
154
|
-
enumerable: true
|
|
155
|
-
});
|
|
149
|
+
const fileOptions = {};
|
|
150
|
+
if (typeof fileData.type === 'string') fileOptions.type = fileData.type;
|
|
151
|
+
if (Number.isFinite(fileData.lastModified)) fileOptions.lastModified = fileData.lastModified;
|
|
156
152
|
|
|
157
|
-
|
|
158
|
-
Object.defineProperty(file, 'size', {
|
|
159
|
-
value: fileData.size,
|
|
160
|
-
writable: true,
|
|
161
|
-
enumerable: true
|
|
162
|
-
});
|
|
153
|
+
const file = new File([""], fileData.name, fileOptions);
|
|
163
154
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
155
|
+
if (fileData.id !== undefined && fileData.id !== null && fileData.id !== '') {
|
|
156
|
+
Object.defineProperty(file, 'id', {
|
|
157
|
+
value: fileData.id,
|
|
158
|
+
writable: true,
|
|
159
|
+
enumerable: true
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (Number.isFinite(fileData.size)) {
|
|
164
|
+
Object.defineProperty(file, 'size', {
|
|
165
|
+
value: fileData.size,
|
|
166
|
+
writable: true,
|
|
167
|
+
enumerable: true
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (fileData.src !== undefined && fileData.src !== null && fileData.src !== '') {
|
|
172
|
+
Object.defineProperty(file, 'src', {
|
|
173
|
+
value: fileData.src,
|
|
174
|
+
writable: true,
|
|
175
|
+
enumerable: true
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (fileData.image !== undefined && fileData.image !== null && fileData.image !== '') {
|
|
180
|
+
Object.defineProperty(file, 'image', {
|
|
181
|
+
value: fileData.image,
|
|
182
|
+
writable: true,
|
|
183
|
+
enumerable: true
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (fileData.customData && typeof fileData.customData === 'object' && !Array.isArray(fileData.customData)) {
|
|
188
|
+
Object.defineProperty(file, 'customData', {
|
|
189
|
+
value: fileData.customData,
|
|
190
|
+
writable: true,
|
|
191
|
+
enumerable: true
|
|
192
|
+
});
|
|
193
|
+
}
|
|
170
194
|
|
|
171
195
|
const fileKey = this._getFileKey(file);
|
|
172
196
|
|
|
@@ -207,32 +231,32 @@ class VGFiles extends VGFilesBase {
|
|
|
207
231
|
});
|
|
208
232
|
}
|
|
209
233
|
|
|
210
|
-
_handleChange(e) {
|
|
211
|
-
const input = e?.target;
|
|
212
|
-
const inputFiles = this._snapshotInputFiles(input);
|
|
213
|
-
this.change(input);
|
|
214
|
-
|
|
215
|
-
if (this._params.ajax) this.uploadAll(this._files);
|
|
216
|
-
|
|
217
|
-
const payload = {
|
|
218
|
-
files: this._files,
|
|
219
|
-
input: e?.target || e?.src || '',
|
|
220
|
-
inputFiles
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
this._triggerCallback('onChange', payload);
|
|
224
|
-
this._triggerEvent('change', {
|
|
225
|
-
files: this._files,
|
|
226
|
-
input: payload.input,
|
|
227
|
-
inputFiles: payload.inputFiles
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
_snapshotInputFiles(input) {
|
|
232
|
-
const files = input?.files;
|
|
233
|
-
if (!files?.length) return [];
|
|
234
|
-
return Array.from(files);
|
|
235
|
-
}
|
|
234
|
+
_handleChange(e) {
|
|
235
|
+
const input = e?.target;
|
|
236
|
+
const inputFiles = this._snapshotInputFiles(input);
|
|
237
|
+
this.change(input);
|
|
238
|
+
|
|
239
|
+
if (this._params.ajax) this.uploadAll(this._files);
|
|
240
|
+
|
|
241
|
+
const payload = {
|
|
242
|
+
files: this._files,
|
|
243
|
+
input: e?.target || e?.src || '',
|
|
244
|
+
inputFiles
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
this._triggerCallback('onChange', payload);
|
|
248
|
+
this._triggerEvent('change', {
|
|
249
|
+
files: this._files,
|
|
250
|
+
input: payload.input,
|
|
251
|
+
inputFiles: payload.inputFiles
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
_snapshotInputFiles(input) {
|
|
256
|
+
const files = input?.files;
|
|
257
|
+
if (!files?.length) return [];
|
|
258
|
+
return Array.from(files);
|
|
259
|
+
}
|
|
236
260
|
|
|
237
261
|
async uploadAll(files) {
|
|
238
262
|
if (!this._params.ajax || !this._params.uploads.route) return;
|
|
@@ -682,15 +706,15 @@ class VGFiles extends VGFilesBase {
|
|
|
682
706
|
|
|
683
707
|
return this._tpl.button([
|
|
684
708
|
this._tpl.i({}, icon, { isHTML: true })
|
|
685
|
-
], 'button', {
|
|
709
|
+
], 'button', this._buildFileDataAttributes(file, {
|
|
686
710
|
type: 'button',
|
|
687
711
|
[action]: 'file',
|
|
688
712
|
'data-name': file.name,
|
|
689
|
-
'data-size': file.size,
|
|
690
|
-
'data-type': file.type,
|
|
691
|
-
'data-last-modified': file.lastModified,
|
|
713
|
+
'data-size': file.size ?? 0,
|
|
714
|
+
'data-type': file.type || '',
|
|
715
|
+
'data-last-modified': file.lastModified || '',
|
|
692
716
|
'data-id': file.id || ''
|
|
693
|
-
});
|
|
717
|
+
}));
|
|
694
718
|
}
|
|
695
719
|
|
|
696
720
|
_renderStat() {
|
|
@@ -874,4 +898,4 @@ EventHandler.on(document, `click.${NAME_KEY}.data.api`, SELECTOR_DATA_DISMISS_AL
|
|
|
874
898
|
}
|
|
875
899
|
});
|
|
876
900
|
|
|
877
|
-
export default VGFiles;
|
|
901
|
+
export default VGFiles;
|
|
@@ -52,6 +52,10 @@ class VGNav extends BaseModule {
|
|
|
52
52
|
breakpoint: 'lg',
|
|
53
53
|
placement: 'horizontal',
|
|
54
54
|
hover: true,
|
|
55
|
+
hoversmoothfirstlevel: {
|
|
56
|
+
enable: false,
|
|
57
|
+
horizontalOnly: true
|
|
58
|
+
},
|
|
55
59
|
animation: {
|
|
56
60
|
enable: true,
|
|
57
61
|
timeout: 700
|
|
@@ -98,6 +102,7 @@ class VGNav extends BaseModule {
|
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
this._openDrops = new Map();
|
|
105
|
+
this._pointerPosition = null;
|
|
101
106
|
this._handleScroll = this._handleScroll.bind(this);
|
|
102
107
|
this._handleResize = this._handleResize.bind(this);
|
|
103
108
|
}
|
|
@@ -333,6 +338,50 @@ class VGNav extends BaseModule {
|
|
|
333
338
|
return vertInView && horInView;
|
|
334
339
|
}
|
|
335
340
|
|
|
341
|
+
_updatePointerPosition(event) {
|
|
342
|
+
if (!event || typeof event.clientX !== 'number' || typeof event.clientY !== 'number') return;
|
|
343
|
+
this._pointerPosition = {
|
|
344
|
+
x: event.clientX,
|
|
345
|
+
y: event.clientY
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
_isHorizontalPointerMove(event) {
|
|
350
|
+
if (!this._pointerPosition || !event) return false;
|
|
351
|
+
|
|
352
|
+
const dx = event.clientX - this._pointerPosition.x;
|
|
353
|
+
const dy = event.clientY - this._pointerPosition.y;
|
|
354
|
+
|
|
355
|
+
return Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 0;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
_isFirstLevelDropdown(drop) {
|
|
359
|
+
return !!drop && !drop.closest('.dropdown-content');
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
_hasDirectDropdownContent(drop) {
|
|
363
|
+
if (!drop || !drop.children) return false;
|
|
364
|
+
return [...drop.children].some((child) => child.classList && child.classList.contains('dropdown-content'));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
_isAdjacentDropdown(drop, relatedDrop) {
|
|
368
|
+
if (!drop || !relatedDrop) return false;
|
|
369
|
+
if (drop.parentElement !== relatedDrop.parentElement) return false;
|
|
370
|
+
return drop.previousElementSibling === relatedDrop || drop.nextElementSibling === relatedDrop;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
_canSmoothSwitchFirstLevel(event, currentDrop, relatedDrop) {
|
|
374
|
+
const smoothParams = this._params.hoversmoothfirstlevel || {};
|
|
375
|
+
if (!smoothParams.enable) return false;
|
|
376
|
+
if (!currentDrop || !relatedDrop || currentDrop === relatedDrop) return false;
|
|
377
|
+
if (!this._isFirstLevelDropdown(currentDrop) || !this._isFirstLevelDropdown(relatedDrop)) return false;
|
|
378
|
+
if (!this._hasDirectDropdownContent(currentDrop) || !this._hasDirectDropdownContent(relatedDrop)) return false;
|
|
379
|
+
if (!this._isAdjacentDropdown(currentDrop, relatedDrop)) return false;
|
|
380
|
+
if (smoothParams.horizontalOnly && !this._isHorizontalPointerMove(event)) return false;
|
|
381
|
+
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
|
|
336
385
|
static init(element, params = {}) {
|
|
337
386
|
const instance = VGNav.getOrCreateInstance(element, params);
|
|
338
387
|
instance.build();
|
|
@@ -340,12 +389,15 @@ class VGNav extends BaseModule {
|
|
|
340
389
|
let drops = Selectors.findAll('.dropdown', instance.navigation);
|
|
341
390
|
|
|
342
391
|
if (instance._params.hover && !isMobileDevice()) {
|
|
392
|
+
EventHandler.on(instance.navigation, `mousemove.${NAME_KEY}.data.api`, function (event) {
|
|
393
|
+
instance._updatePointerPosition(event);
|
|
394
|
+
});
|
|
395
|
+
|
|
343
396
|
[...drops].forEach(function (el) {
|
|
344
397
|
let currentElem = null;
|
|
345
398
|
|
|
346
399
|
EventHandler.on(el, EVENT_MOUSEOVER_DATA_API, function (event) {
|
|
347
400
|
if (currentElem) return;
|
|
348
|
-
VGNav.hideOpenDrops(event);
|
|
349
401
|
|
|
350
402
|
let target = event.target.closest('.dropdown');
|
|
351
403
|
if (!target) return;
|
|
@@ -353,17 +405,31 @@ class VGNav extends BaseModule {
|
|
|
353
405
|
if (!instance.navigation.contains(target)) return;
|
|
354
406
|
currentElem = target;
|
|
355
407
|
|
|
408
|
+
const previousDrop = event.relatedTarget?.closest('.dropdown');
|
|
409
|
+
const useSmoothSwitch = instance._canSmoothSwitchFirstLevel(event, target, previousDrop);
|
|
410
|
+
|
|
411
|
+
if (!useSmoothSwitch) {
|
|
412
|
+
VGNav.hideOpenDrops(event);
|
|
413
|
+
}
|
|
414
|
+
|
|
356
415
|
let relatedTarget = {
|
|
357
416
|
relatedTarget: target
|
|
358
417
|
};
|
|
359
418
|
|
|
360
419
|
instance.show(relatedTarget);
|
|
420
|
+
|
|
421
|
+
if (useSmoothSwitch && previousDrop && previousDrop.classList.contains(CLASS_NAME_ACTIVE)) {
|
|
422
|
+
instance.hide({ relatedTarget: previousDrop });
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
instance._updatePointerPosition(event);
|
|
361
426
|
});
|
|
362
427
|
|
|
363
428
|
EventHandler.on(el, EVENT_MOUSEOUT_DATA_API, function (event) {
|
|
364
429
|
if (!currentElem) return;
|
|
365
430
|
|
|
366
|
-
let
|
|
431
|
+
let nextDrop = event.relatedTarget?.closest('.dropdown'),
|
|
432
|
+
relatedTarget = nextDrop,
|
|
367
433
|
elm = currentElem;
|
|
368
434
|
|
|
369
435
|
while (relatedTarget) {
|
|
@@ -371,8 +437,15 @@ class VGNav extends BaseModule {
|
|
|
371
437
|
relatedTarget = relatedTarget.parentNode;
|
|
372
438
|
}
|
|
373
439
|
|
|
440
|
+
if (instance._canSmoothSwitchFirstLevel(event, elm, nextDrop)) {
|
|
441
|
+
currentElem = null;
|
|
442
|
+
instance._updatePointerPosition(event);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
|
|
374
446
|
currentElem = null;
|
|
375
447
|
instance.hide({ relatedTarget: relatedTarget, elm: elm });
|
|
448
|
+
instance._updatePointerPosition(event);
|
|
376
449
|
});
|
|
377
450
|
});
|
|
378
451
|
}
|