vgapp 1.5.6 → 1.5.8

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 (109) hide show
  1. package/app/langs/en/buttons.json +17 -17
  2. package/app/langs/en/messages.json +36 -36
  3. package/app/langs/ru/buttons.json +17 -17
  4. package/app/langs/ru/messages.json +1 -1
  5. package/app/modules/vgalert/js/vgalert.js +6 -3
  6. package/app/modules/vgalert/scss/_variables.scss +16 -16
  7. package/app/modules/vgcollapse/scss/_variables.scss +7 -7
  8. package/app/modules/vgcollapse/scss/vgcollapse.scss +5 -5
  9. package/app/modules/vgdropdown/scss/_variables.scss +14 -14
  10. package/app/modules/vgdropdown/scss/vgdropdown.scss +5 -5
  11. package/app/modules/vgfilepreview/js/i18n.js +56 -56
  12. package/app/modules/vgfilepreview/js/renderers/image-modal.js +145 -145
  13. package/app/modules/vgfilepreview/js/renderers/image.js +92 -92
  14. package/app/modules/vgfilepreview/js/renderers/index.js +19 -19
  15. package/app/modules/vgfilepreview/js/renderers/office-modal.js +168 -168
  16. package/app/modules/vgfilepreview/js/renderers/office.js +79 -79
  17. package/app/modules/vgfilepreview/js/renderers/pdf-modal.js +260 -260
  18. package/app/modules/vgfilepreview/js/renderers/pdf.js +76 -76
  19. package/app/modules/vgfilepreview/js/renderers/playlist.js +71 -71
  20. package/app/modules/vgfilepreview/js/renderers/text-modal.js +343 -343
  21. package/app/modules/vgfilepreview/js/renderers/text.js +83 -83
  22. package/app/modules/vgfilepreview/js/renderers/zip-modal.js +522 -522
  23. package/app/modules/vgfilepreview/js/renderers/zip.js +89 -89
  24. package/app/modules/vgfilepreview/readme.md +68 -68
  25. package/app/modules/vgfiles/js/base.js +189 -194
  26. package/app/modules/vgfiles/js/droppable.js +17 -17
  27. package/app/modules/vgfiles/js/render.js +153 -153
  28. package/app/modules/vgfiles/js/sortable.js +52 -52
  29. package/app/modules/vgfiles/js/vgfiles.js +43 -43
  30. package/app/modules/vgfiles/readme.md +123 -123
  31. package/app/modules/vgfiles/scss/_mixins.scss +13 -13
  32. package/app/modules/vgfiles/scss/_variables.scss +10 -10
  33. package/app/modules/vgfiles/scss/vgfiles.scss +28 -28
  34. package/app/modules/vgformsender/js/hideshowpass.js +22 -22
  35. package/app/modules/vgformsender/readme.md +1 -1
  36. package/app/modules/vgformsender/scss/_variables.scss +5 -5
  37. package/app/modules/vgformsender/scss/vgformsender.scss +42 -42
  38. package/app/modules/vglawcookie/js/vglawcookie.js +19 -19
  39. package/app/modules/vglawcookie/scss/_variables.scss +13 -13
  40. package/app/modules/vglawcookie/scss/vglawcookie.scss +5 -5
  41. package/app/modules/vgmodal/js/vgmodal.interaction.js +216 -216
  42. package/app/modules/vgmodal/js/vgmodal.js +6 -0
  43. package/app/modules/vgmodal/js/vgmodal.minimized.js +227 -227
  44. package/app/modules/vgmodal/scss/_variables.scss +17 -17
  45. package/app/modules/vgmodal/scss/vgmodal.scss +77 -77
  46. package/app/modules/vgnav/js/vgnav.js +62 -62
  47. package/app/modules/vgnav/scss/_breakpoints.scss +36 -36
  48. package/app/modules/vgnav/scss/_hamburger.scss +5 -5
  49. package/app/modules/vgnav/scss/_placement.scss +5 -5
  50. package/app/modules/vgnav/scss/_toggle.scss +5 -5
  51. package/app/modules/vgnav/scss/_variables.scss +26 -26
  52. package/app/modules/vgnav/scss/vgnav.scss +41 -41
  53. package/app/modules/vgnestable/README.md +39 -39
  54. package/app/modules/vgrangeslider/js/skins.js +8 -8
  55. package/app/modules/vgrangeslider/js/vgrangeslider.js +29 -29
  56. package/app/modules/vgrangeslider/scss/_variables.scss +55 -55
  57. package/app/modules/vgrangeslider/scss/vgrangeslider.scss +239 -239
  58. package/app/modules/vgrollup/js/vgrollup.js +15 -15
  59. package/app/modules/vgrollup/scss/_variables.scss +15 -15
  60. package/app/modules/vgrollup/scss/vgrollup.scss +6 -6
  61. package/app/modules/vgselect/js/handlers.js +5 -52
  62. package/app/modules/vgselect/js/vgselect.js +128 -40
  63. package/app/modules/vgselect/readme.md +11 -11
  64. package/app/modules/vgselect/scss/_variables.scss +51 -51
  65. package/app/modules/vgselect/scss/vgselect.scss +1 -1
  66. package/app/modules/vgsidebar/js/vgsidebar.js +16 -5
  67. package/app/modules/vgsidebar/scss/_variables.scss +10 -10
  68. package/app/modules/vgsidebar/scss/vgsidebar.scss +5 -5
  69. package/app/modules/vgspy/js/vgspy.js +28 -7
  70. package/app/modules/vgtable/js/_filters.js +10 -6
  71. package/app/modules/vgtable/js/_options.js +16 -16
  72. package/app/modules/vgtable/js/_pagination.js +118 -118
  73. package/app/modules/vgtable/js/_responsive.js +79 -79
  74. package/app/modules/vgtable/js/_sticky-header.js +349 -280
  75. package/app/modules/vgtable/js/vgtable.js +98 -98
  76. package/app/modules/vgtable/scss/_sorting.scss +6 -6
  77. package/app/modules/vgtable/scss/_sticky-header.scss +31 -31
  78. package/app/modules/vgtable/scss/_variables.scss +6 -4
  79. package/app/modules/vgtable/scss/vgtable.scss +8 -4
  80. package/app/modules/vgtabs/js/vgtabs.js +82 -57
  81. package/app/modules/vgtabs/scss/_variables.scss +23 -23
  82. package/app/modules/vgtabs/scss/vgtabs.scss +5 -5
  83. package/app/modules/vgtoast/js/vgtoast.js +11 -5
  84. package/app/modules/vgtoast/scss/_variables.scss +13 -13
  85. package/app/modules/vgtoast/scss/vgtoast.scss +5 -5
  86. package/app/modules/vgtooltip/js/vgtooltip.js +121 -111
  87. package/app/modules/vgtooltip/scss/_variables.scss +10 -10
  88. package/app/modules/vgtooltip/scss/vgtooltip.scss +5 -5
  89. package/app/utils/js/components/audio-metadata.js +240 -240
  90. package/app/utils/js/components/backdrop.js +116 -116
  91. package/app/utils/js/components/file-icon.js +5 -0
  92. package/app/utils/js/components/file-preview.js +304 -304
  93. package/app/utils/js/components/responsive.js +165 -165
  94. package/app/utils/js/components/sanitize.js +150 -150
  95. package/app/utils/js/components/video-metadata.js +140 -140
  96. package/app/utils/scss/_backdrop.scss +5 -5
  97. package/app/utils/scss/_theme-default.scss +12 -12
  98. package/app/utils/scss/_theme.scss +7 -7
  99. package/app/utils/scss/default.scss +9 -9
  100. package/app/utils/scss/functions.scss +21 -21
  101. package/app/utils/scss/index.scss +4 -4
  102. package/app/utils/scss/mixin.scss +16 -16
  103. package/app/utils/scss/variables.scss +147 -147
  104. package/build/vgapp.css +1 -1
  105. package/build/vgapp.css.map +1 -1
  106. package/build/vgapp.js +1 -1
  107. package/build/vgapp.js.map +1 -1
  108. package/index.js +7 -7
  109. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Описание: базовая логика выбора, валидации и отображения файлов VGFiles.
3
- * Возможности: управляет набором файлов, ошибками, списками, кастомными действиями, статистикой и скрытыми полями формы.
2
+ * Описание: базовая логика выбора, валидации и отображения файлов VGFiles.
3
+ * Возможности: управляет файлами, валидацией, списками, миниатюрами и иконками, действиями, статистикой и скрытыми полями.
4
4
  */
5
5
  import BaseModule from "../../base-module";
6
6
  import {mergeDeepObject} from "../../../utils/js/functions";
@@ -9,6 +9,7 @@ import {lang_messages} from "../../../utils/js/components/lang";
9
9
  import {Classes, Manipulator} from "../../../utils/js/dom/manipulator";
10
10
  import Selectors from "../../../utils/js/dom/selectors";
11
11
  import {getSVG} from "../../module-fn";
12
+ import {resolveFileIconName} from "../../../utils/js/components/file-icon";
12
13
  import VGFilePreview from "../../vgfilepreview";
13
14
  import {extractAudioMetadata} from "../../../utils/js/components/audio-metadata";
14
15
 
@@ -26,10 +27,10 @@ class VGFilesBase extends BaseModule {
26
27
  this._isInitialized = true;
27
28
 
28
29
  this._tpl = Html('dom');
29
- this._files = [];
30
- this._errors = new Set();
31
- this._validationErrorsByKey = new Map();
32
- this._fileObjectUrls = new Map();
30
+ this._files = [];
31
+ this._errors = new Set();
32
+ this._validationErrorsByKey = new Map();
33
+ this._fileObjectUrls = new Map();
33
34
  this._audioMetaPromises = new Map();
34
35
 
35
36
  this._nodes = {
@@ -198,10 +199,10 @@ class VGFilesBase extends BaseModule {
198
199
  filesToProcess = this._mergeFilesByOrder(incoming, this._files, Boolean(this._params.prepend));
199
200
  }
200
201
 
201
- this._files = this._filterFiles(filesToProcess);
202
- this._syncSingleInputSubmission();
203
-
204
- this._renderErrors();
202
+ this._files = this._filterFiles(filesToProcess);
203
+ this._syncSingleInputSubmission();
204
+
205
+ this._renderErrors();
205
206
  this._enrichAudioMetadata(this._files);
206
207
 
207
208
  return this._files;
@@ -282,119 +283,119 @@ class VGFilesBase extends BaseModule {
282
283
  return attrs;
283
284
  }
284
285
 
285
- _filterFiles(files) {
286
- this._errors.clear();
287
- this._validationErrorsByKey.clear();
288
- const { count, sizes, total } = this._params.limits;
289
- const maxSize = sizes * 1024 * 1024;
290
- const maxTotalSize = total * 1024 * 1024;
291
-
292
- let currentTotalSize = 0;
293
- let validCount = 0;
294
- const visibleFiles = [];
295
-
296
- for (const file of files) {
297
- const fileErrors = new Set();
298
-
299
- if (count > 0 && validCount >= count) {
300
- this._errors.add('is-count');
301
- continue;
302
- }
303
-
304
- if (this._params.types.length && !this._params.types.includes(file.type)) {
305
- fileErrors.add('is-types');
306
- }
307
-
308
- if (file.size > maxSize) {
309
- fileErrors.add('is-sizes');
310
- }
311
-
312
- if (!fileErrors.size && maxTotalSize > 0 && currentTotalSize + file.size > maxTotalSize) {
313
- fileErrors.add('is-total-size');
314
- }
315
-
316
- visibleFiles.push(file);
317
-
318
- if (fileErrors.size) {
319
- fileErrors.forEach(error => this._errors.add(error));
320
- this._validationErrorsByKey.set(this._getFileKey(file), fileErrors);
321
- continue;
322
- }
323
-
324
- validCount += 1;
325
- currentTotalSize += file.size;
326
- }
327
-
328
- return visibleFiles;
329
- }
330
-
331
- _isFileValid(file) {
332
- return !this._validationErrorsByKey.has(this._getFileKey(file));
333
- }
334
-
335
- _syncSingleInputSubmission() {
336
- if (Number(this._params?.limits?.count) !== 1) return;
337
-
338
- const selectedFile = this._files[0];
339
- const canSubmit = !this._params.ajax && (!selectedFile || this._isFileValid(selectedFile));
340
- this._preventOriginalInputFromSubmit(canSubmit);
341
- }
342
-
343
- _getFileValidationMessages(file) {
344
- const errors = this._validationErrorsByKey.get(this._getFileKey(file));
345
- if (!errors?.size) return [];
346
-
347
- const messages = lang_messages(this._params.lang, 'files') || this._getFallbackErrors();
348
- return Array.from(errors, error => messages[error] || error);
349
- }
350
-
351
- _getFileValidationLabel(file) {
352
- const messages = this._getFileValidationMessages(file);
353
- if (!messages.length) return '';
354
-
355
- const fileName = this._getShortValidationFileName(file?.name);
356
- return [fileName, messages.join('. ')].filter(Boolean).join(' — ');
357
- }
358
-
359
- _getFullFileValidationLabel(file) {
360
- const messages = this._getFileValidationMessages(file);
361
- if (!messages.length) return '';
362
-
363
- const fileName = String(file?.name || '').trim();
364
- return [fileName, messages.join('. ')].filter(Boolean).join(' — ');
365
- }
366
-
367
- _getShortValidationFileName(fileName) {
368
- const name = String(fileName || '').trim();
369
- if (name.length <= 24) return name;
370
-
371
- const dotIndex = name.lastIndexOf('.');
372
- const hasExtension = dotIndex > 0 && name.length - dotIndex <= 10;
373
- const baseName = hasExtension ? name.slice(0, dotIndex) : name;
374
- const extension = hasExtension ? name.slice(dotIndex) : '';
375
-
376
- return `${baseName.slice(0, 5)}...${extension}`;
377
- }
378
-
379
- _decorateValidationItem(item, file) {
380
- if (this._isFileValid(file)) return item;
381
-
382
- Classes.add(item, 'failing');
383
- Classes.add(item, 'validation-error');
384
-
385
- return item;
386
- }
387
-
388
- _forgetFileValidation(file) {
389
- if (!file) return;
390
-
391
- this._validationErrorsByKey.delete(this._getFileKey(file));
392
- this._errors.clear();
393
- this._validationErrorsByKey.forEach(errors => {
394
- errors.forEach(error => this._errors.add(error));
395
- });
396
- this._renderErrors();
397
- }
286
+ _filterFiles(files) {
287
+ this._errors.clear();
288
+ this._validationErrorsByKey.clear();
289
+ const { count, sizes, total } = this._params.limits;
290
+ const maxSize = sizes * 1024 * 1024;
291
+ const maxTotalSize = total * 1024 * 1024;
292
+
293
+ let currentTotalSize = 0;
294
+ let validCount = 0;
295
+ const visibleFiles = [];
296
+
297
+ for (const file of files) {
298
+ const fileErrors = new Set();
299
+
300
+ if (count > 0 && validCount >= count) {
301
+ this._errors.add('is-count');
302
+ continue;
303
+ }
304
+
305
+ if (this._params.types.length && !this._params.types.includes(file.type)) {
306
+ fileErrors.add('is-types');
307
+ }
308
+
309
+ if (file.size > maxSize) {
310
+ fileErrors.add('is-sizes');
311
+ }
312
+
313
+ if (!fileErrors.size && maxTotalSize > 0 && currentTotalSize + file.size > maxTotalSize) {
314
+ fileErrors.add('is-total-size');
315
+ }
316
+
317
+ visibleFiles.push(file);
318
+
319
+ if (fileErrors.size) {
320
+ fileErrors.forEach(error => this._errors.add(error));
321
+ this._validationErrorsByKey.set(this._getFileKey(file), fileErrors);
322
+ continue;
323
+ }
324
+
325
+ validCount += 1;
326
+ currentTotalSize += file.size;
327
+ }
328
+
329
+ return visibleFiles;
330
+ }
331
+
332
+ _isFileValid(file) {
333
+ return !this._validationErrorsByKey.has(this._getFileKey(file));
334
+ }
335
+
336
+ _syncSingleInputSubmission() {
337
+ if (Number(this._params?.limits?.count) !== 1) return;
338
+
339
+ const selectedFile = this._files[0];
340
+ const canSubmit = !this._params.ajax && (!selectedFile || this._isFileValid(selectedFile));
341
+ this._preventOriginalInputFromSubmit(canSubmit);
342
+ }
343
+
344
+ _getFileValidationMessages(file) {
345
+ const errors = this._validationErrorsByKey.get(this._getFileKey(file));
346
+ if (!errors?.size) return [];
347
+
348
+ const messages = lang_messages(this._params.lang, 'files') || this._getFallbackErrors();
349
+ return Array.from(errors, error => messages[error] || error);
350
+ }
351
+
352
+ _getFileValidationLabel(file) {
353
+ const messages = this._getFileValidationMessages(file);
354
+ if (!messages.length) return '';
355
+
356
+ const fileName = this._getShortValidationFileName(file?.name);
357
+ return [fileName, messages.join('. ')].filter(Boolean).join(' — ');
358
+ }
359
+
360
+ _getFullFileValidationLabel(file) {
361
+ const messages = this._getFileValidationMessages(file);
362
+ if (!messages.length) return '';
363
+
364
+ const fileName = String(file?.name || '').trim();
365
+ return [fileName, messages.join('. ')].filter(Boolean).join(' — ');
366
+ }
367
+
368
+ _getShortValidationFileName(fileName) {
369
+ const name = String(fileName || '').trim();
370
+ if (name.length <= 24) return name;
371
+
372
+ const dotIndex = name.lastIndexOf('.');
373
+ const hasExtension = dotIndex > 0 && name.length - dotIndex <= 10;
374
+ const baseName = hasExtension ? name.slice(0, dotIndex) : name;
375
+ const extension = hasExtension ? name.slice(dotIndex) : '';
376
+
377
+ return `${baseName.slice(0, 5)}...${extension}`;
378
+ }
379
+
380
+ _decorateValidationItem(item, file) {
381
+ if (this._isFileValid(file)) return item;
382
+
383
+ Classes.add(item, 'failing');
384
+ Classes.add(item, 'validation-error');
385
+
386
+ return item;
387
+ }
388
+
389
+ _forgetFileValidation(file) {
390
+ if (!file) return;
391
+
392
+ this._validationErrorsByKey.delete(this._getFileKey(file));
393
+ this._errors.clear();
394
+ this._validationErrorsByKey.forEach(errors => {
395
+ errors.forEach(error => this._errors.add(error));
396
+ });
397
+ this._renderErrors();
398
+ }
398
399
 
399
400
  _getSizes(size, isArray = false) {
400
401
  const totalSize = isArray ? this._files.reduce((acc, f) => acc + f.size, 0) : size;
@@ -418,12 +419,12 @@ class VGFilesBase extends BaseModule {
418
419
  }
419
420
  }
420
421
 
421
- _renderErrors() {
422
- if (!this._errors.size) {
423
- const $errorCont = Selectors.find(`.${this._getClass('errors')}`, this._element);
424
- if ($errorCont) $errorCont.remove();
425
- return;
426
- }
422
+ _renderErrors() {
423
+ if (!this._errors.size) {
424
+ const $errorCont = Selectors.find(`.${this._getClass('errors')}`, this._element);
425
+ if ($errorCont) $errorCont.remove();
426
+ return;
427
+ }
427
428
 
428
429
  const messages = lang_messages(this._params.lang, 'files') || this._getFallbackErrors();
429
430
  let $errorCont = Selectors.find(`.${this._getClass('errors')}`, this._element);
@@ -435,32 +436,32 @@ class VGFilesBase extends BaseModule {
435
436
  $errorCont.innerHTML = '';
436
437
  }
437
438
 
438
- if (this._validationErrorsByKey.size) {
439
- const renderedErrors = new Set();
440
- this._files.forEach(file => {
441
- const msg = this._getFileValidationLabel(file);
442
- if (!msg) return;
443
-
444
- const fileErrors = this._validationErrorsByKey.get(this._getFileKey(file));
445
- fileErrors?.forEach(error => renderedErrors.add(error));
446
- const fullMsg = this._getFullFileValidationLabel(file);
447
- const attrs = { class: 'error-item' };
448
- if (fullMsg && fullMsg !== msg) attrs.title = fullMsg;
449
- $errorCont.append(this._tpl.span(attrs, msg));
450
- });
451
-
452
- this._errors.forEach(errKey => {
453
- if (renderedErrors.has(errKey)) return;
454
- const msg = messages[errKey] || errKey;
455
- $errorCont.append(this._tpl.span({ class: 'error-item' }, msg));
456
- });
457
- return;
458
- }
459
-
460
- this._errors.forEach(errKey => {
461
- const msg = messages[errKey] || errKey;
462
- $errorCont.append(this._tpl.span({ class: 'error-item' }, msg));
463
- });
439
+ if (this._validationErrorsByKey.size) {
440
+ const renderedErrors = new Set();
441
+ this._files.forEach(file => {
442
+ const msg = this._getFileValidationLabel(file);
443
+ if (!msg) return;
444
+
445
+ const fileErrors = this._validationErrorsByKey.get(this._getFileKey(file));
446
+ fileErrors?.forEach(error => renderedErrors.add(error));
447
+ const fullMsg = this._getFullFileValidationLabel(file);
448
+ const attrs = { class: 'error-item' };
449
+ if (fullMsg && fullMsg !== msg) attrs.title = fullMsg;
450
+ $errorCont.append(this._tpl.span(attrs, msg));
451
+ });
452
+
453
+ this._errors.forEach(errKey => {
454
+ if (renderedErrors.has(errKey)) return;
455
+ const msg = messages[errKey] || errKey;
456
+ $errorCont.append(this._tpl.span({ class: 'error-item' }, msg));
457
+ });
458
+ return;
459
+ }
460
+
461
+ this._errors.forEach(errKey => {
462
+ const msg = messages[errKey] || errKey;
463
+ $errorCont.append(this._tpl.span({ class: 'error-item' }, msg));
464
+ });
464
465
  }
465
466
 
466
467
  _getFallbackErrors() {
@@ -522,7 +523,7 @@ class VGFilesBase extends BaseModule {
522
523
  };
523
524
  }
524
525
 
525
- _renderUIDropList(files) {
526
+ _renderUIDropList(files) {
526
527
  if (!this._nodes.drop) return;
527
528
 
528
529
  let $list = Selectors.find(`.${this._getClass('drop-list')}`, this._nodes.drop);
@@ -535,10 +536,10 @@ class VGFilesBase extends BaseModule {
535
536
  const fragment = document.createDocumentFragment();
536
537
 
537
538
  files.forEach((file) => {
538
- let classes = [...$itemsTemplateClasses];
539
+ let classes = [...$itemsTemplateClasses];
539
540
 
540
- if (this._params.detach) classes.push('with-remove')
541
- if (this._params.customButtons?.hideOnUploadState) classes.push('hide-custom-buttons-on-upload-state')
541
+ if (this._params.detach) classes.push('with-remove')
542
+ if (this._params.customButtons?.hideOnUploadState) classes.push('hide-custom-buttons-on-upload-state')
542
543
  if (this._params.sortable.enabled) classes.push('with-sortable')
543
544
  if (this._params.limits.count === 1) {
544
545
  classes.push('single');
@@ -566,7 +567,7 @@ class VGFilesBase extends BaseModule {
566
567
  parts
567
568
  );
568
569
 
569
- fragment.appendChild(this._decorateValidationItem($li, file));
570
+ fragment.appendChild(this._decorateValidationItem($li, file));
570
571
  });
571
572
 
572
573
  $list.innerHTML = '';
@@ -707,13 +708,13 @@ class VGFilesBase extends BaseModule {
707
708
  return '';
708
709
  }
709
710
 
710
- _createInfoListItem(file, i, itemsTemplate, itemTemplateClasses, fileKey, signature) {
711
+ _createInfoListItem(file, i, itemsTemplate, itemTemplateClasses, fileKey, signature) {
711
712
  let classes = [...itemTemplateClasses];
712
713
 
713
714
  if (this._params.image) classes.push('with-image');
714
715
  if (this._params.info) classes.push('with-info');
715
- if (this._params.detach) classes.push('with-remove')
716
- if (this._params.customButtons?.hideOnUploadState) classes.push('hide-custom-buttons-on-upload-state')
716
+ if (this._params.detach) classes.push('with-remove')
717
+ if (this._params.customButtons?.hideOnUploadState) classes.push('hide-custom-buttons-on-upload-state')
717
718
  if (this._params.sortable.enabled) classes.push('with-sortable');
718
719
  const previewPath = this._resolveFilePreviewPath(file);
719
720
  const displayName = this._resolveDisplayName(file);
@@ -741,12 +742,12 @@ class VGFilesBase extends BaseModule {
741
742
  liAttrs['data-vg-filepreview-display-name'] = displayName || file.name || '';
742
743
  }
743
744
 
744
- const item = this._tpl.li(
745
- this._buildFileDataAttributes(file, liAttrs),
746
- parts
747
- );
748
-
749
- return this._decorateValidationItem(item, file);
745
+ const item = this._tpl.li(
746
+ this._buildFileDataAttributes(file, liAttrs),
747
+ parts
748
+ );
749
+
750
+ return this._decorateValidationItem(item, file);
750
751
  }
751
752
 
752
753
  _renderTemplatePart(element, file, index = null, options = {}) {
@@ -815,31 +816,25 @@ class VGFilesBase extends BaseModule {
815
816
 
816
817
  _renderUIImage(file) {
817
818
  const $container = this._tpl.div({ class: 'file-image' });
818
-
819
- const src = file?.src || file?.image;
820
- if (src) {
821
- $container.appendChild(this._tpl.img(src, file.name || '', { class: 'file-preview' }));
822
- return $container;
823
- }
824
-
819
+ const renderIcon = () => {
820
+ const icon = this._getIconByFileType(file);
821
+ $container.replaceChildren(this._tpl.i({}, icon, { isHTML: true }));
822
+ };
825
823
  const customData = this._getFileCustomData(file);
826
- const audioCover = String(customData.audioCover || '').trim();
827
- if (audioCover) {
828
- $container.appendChild(this._tpl.img(audioCover, this._resolveDisplayName(file), { class: 'file-preview' }));
829
- return $container;
824
+ let imageSrc = String(file?.image || '').trim() || String(customData.audioCover || '').trim();
825
+
826
+ if (!imageSrc && resolveFileIconName({ type: file?.type, name: file?.name || file?.src }) === 'file-image') {
827
+ imageSrc = String(file?.src || '').trim() || this._getFileObjectUrl(file);
830
828
  }
831
829
 
832
- if (file?.type && file.type.startsWith('image/')) {
833
- const objectUrl = this._getFileObjectUrl(file);
834
- if (!objectUrl) {
835
- return $container;
836
- }
837
- $container.appendChild(this._tpl.img(objectUrl, file.name, { class: 'file-preview' }));
838
- return $container;
830
+ if (imageSrc) {
831
+ const image = this._tpl.img(imageSrc, this._resolveDisplayName(file), { class: 'file-preview' });
832
+ image.addEventListener('error', renderIcon, { once: true });
833
+ $container.appendChild(image);
834
+ } else {
835
+ renderIcon();
839
836
  }
840
837
 
841
- const icon = this._getIconByFileType(file);
842
- $container.appendChild(this._tpl.i({}, icon, { isHTML: true }));
843
838
  return $container;
844
839
  }
845
840
 
@@ -1028,7 +1023,7 @@ class VGFilesBase extends BaseModule {
1028
1023
 
1029
1024
  if (isSingle) return;
1030
1025
 
1031
- files.filter(file => this._isFileValid(file)).forEach((file, index) => {
1026
+ files.filter(file => this._isFileValid(file)).forEach((file, index) => {
1032
1027
  const input = document.createElement('input');
1033
1028
  input.type = 'file';
1034
1029
  input.name = `${baseName}[${index}]`;
@@ -1070,10 +1065,10 @@ class VGFilesBase extends BaseModule {
1070
1065
  if (resetInput) {
1071
1066
  this._resetFileInput();
1072
1067
  }
1073
- this._cleanupFakeInputs();
1074
- this._cleanupErrors();
1075
- this._validationErrorsByKey.clear();
1076
- this._files = [];
1068
+ this._cleanupFakeInputs();
1069
+ this._cleanupErrors();
1070
+ this._validationErrorsByKey.clear();
1071
+ this._files = [];
1077
1072
 
1078
1073
  if (this._nodes.info) {
1079
1074
  Classes.remove(this._nodes.info, 'show');
@@ -5,9 +5,9 @@
5
5
  import { isElement } from "../../../utils/js/functions";
6
6
  import EventHandler from "../../../utils/js/dom/event";
7
7
  import Selectors from "../../../utils/js/dom/selectors";
8
- import { Classes } from "../../../utils/js/dom/manipulator";
9
- import BaseModule from "../../base-module";
10
- import {isVGFilesSortableDragActive, VG_FILES_SORTABLE_DATA_TYPE} from "./sortable";
8
+ import { Classes } from "../../../utils/js/dom/manipulator";
9
+ import BaseModule from "../../base-module";
10
+ import {isVGFilesSortableDragActive, VG_FILES_SORTABLE_DATA_TYPE} from "./sortable";
11
11
 
12
12
  const CLASS_NAME_CONTAINER = 'vg-files';
13
13
  const CLASS_NAME_DROP = `${CLASS_NAME_CONTAINER}-drop`;
@@ -254,20 +254,20 @@ class VGFilesDroppable extends BaseModule {
254
254
  }
255
255
  }
256
256
 
257
- _isSortableDrag(e) {
258
- if (isVGFilesSortableDragActive()) return true;
259
- if (document.querySelector('.dragging')) return true;
260
-
261
- try {
262
- const dataTransfer = e?.dataTransfer;
263
- const types = Array.from(dataTransfer?.types || []);
264
- if (types.includes(VG_FILES_SORTABLE_DATA_TYPE)) return true;
265
-
266
- return (dataTransfer?.getData?.(VG_FILES_SORTABLE_DATA_TYPE) || '') === '1' ||
267
- (dataTransfer?.getData?.('text/plain') || '') === 'vgsortable';
268
- } catch (_) {
269
- return false;
270
- }
257
+ _isSortableDrag(e) {
258
+ if (isVGFilesSortableDragActive()) return true;
259
+ if (document.querySelector('.dragging')) return true;
260
+
261
+ try {
262
+ const dataTransfer = e?.dataTransfer;
263
+ const types = Array.from(dataTransfer?.types || []);
264
+ if (types.includes(VG_FILES_SORTABLE_DATA_TYPE)) return true;
265
+
266
+ return (dataTransfer?.getData?.(VG_FILES_SORTABLE_DATA_TYPE) || '') === '1' ||
267
+ (dataTransfer?.getData?.('text/plain') || '') === 'vgsortable';
268
+ } catch (_) {
269
+ return false;
270
+ }
271
271
  }
272
272
 
273
273
  _getVisibleDropZonesInViewport() {