vgapp 1.4.8 → 1.5.0

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 (86) hide show
  1. package/README.md +24 -2
  2. package/app/modules/base-module.js +14 -2
  3. package/app/modules/module-fn.js +5 -4
  4. package/app/modules/vgalert/js/vgalert.js +21 -1
  5. package/app/modules/vgalert/scss/vgalert.scss +48 -0
  6. package/app/modules/vgdropdown/js/vgdropdown.js +58 -27
  7. package/app/modules/vgfilepreview/js/renderers/video-modal.js +5 -1
  8. package/app/modules/vgfilepreview/js/renderers/video.js +6 -1
  9. package/app/modules/vgfilepreview/js/vgfilepreview.js +5 -1
  10. package/app/modules/vgfiles/js/base.js +29 -15
  11. package/app/modules/vgfiles/js/droppable.js +37 -31
  12. package/app/modules/vgfiles/js/vgfiles.js +10 -4
  13. package/app/modules/vgfiles/scss/_mixins.scss +12 -2
  14. package/app/modules/vgfiles/scss/_variables.scss +3 -1
  15. package/app/modules/vgfiles/scss/vgfiles.scss +55 -10
  16. package/app/modules/vgformsender/js/vgformsender.js +5 -2
  17. package/app/modules/vgloadmore/js/vgloadmore.js +35 -7
  18. package/app/modules/vgnestable/js/vgnestable.js +140 -0
  19. package/app/modules/vgnestable/scss/_variables.scss +16 -9
  20. package/app/modules/vgnestable/scss/vgnestable.scss +35 -2
  21. package/app/modules/vgtable/index.js +3 -0
  22. package/app/modules/vgtable/js/_columns.js +567 -0
  23. package/app/modules/vgtable/js/_expandable.js +242 -0
  24. package/app/modules/vgtable/js/_filters.js +264 -0
  25. package/app/modules/vgtable/js/_fixed-columns.js +249 -0
  26. package/app/modules/vgtable/js/_i18n.js +75 -0
  27. package/app/modules/vgtable/js/_options.js +428 -0
  28. package/app/modules/vgtable/js/_pagination.js +491 -0
  29. package/app/modules/vgtable/js/_panning.js +124 -0
  30. package/app/modules/vgtable/js/_params-groups.js +21 -0
  31. package/app/modules/vgtable/js/_remote.js +395 -0
  32. package/app/modules/vgtable/js/_row-reorder.js +268 -0
  33. package/app/modules/vgtable/js/_search.js +134 -0
  34. package/app/modules/vgtable/js/_selection.js +113 -0
  35. package/app/modules/vgtable/js/_skeleton.js +123 -0
  36. package/app/modules/vgtable/js/_sorting.js +340 -0
  37. package/app/modules/vgtable/js/_states.js +143 -0
  38. package/app/modules/vgtable/js/_sticky-header.js +218 -0
  39. package/app/modules/vgtable/js/_url-state.js +178 -0
  40. package/app/modules/vgtable/js/vgtable.js +1444 -0
  41. package/app/modules/vgtable/readme.md +922 -0
  42. package/app/modules/vgtable/scss/_columns.scss +52 -0
  43. package/app/modules/vgtable/scss/_expandable.scss +54 -0
  44. package/app/modules/vgtable/scss/_fixed-columns.scss +117 -0
  45. package/app/modules/vgtable/scss/_pagination.scss +191 -0
  46. package/app/modules/vgtable/scss/_row-reorder.scss +45 -0
  47. package/app/modules/vgtable/scss/_skeleton.scss +65 -0
  48. package/app/modules/vgtable/scss/_sorting.scss +89 -0
  49. package/app/modules/vgtable/scss/_states.scss +70 -0
  50. package/app/modules/vgtable/scss/_sticky-header.scss +127 -0
  51. package/app/modules/vgtable/scss/_variables.scss +296 -0
  52. package/app/modules/vgtable/scss/vgtable.scss +206 -0
  53. package/app/utils/js/components/placement.js +64 -10
  54. package/app/vgapp.js +3 -3
  55. package/build/vgapp.css +1 -1
  56. package/build/vgapp.css.map +1 -1
  57. package/build/vgapp.js +1 -1
  58. package/build/vgapp.js.map +1 -1
  59. package/index.js +1 -1
  60. package/index.scss +3 -3
  61. package/package.json +4 -4
  62. package/app/modules/vgdynamictable/index.js +0 -4
  63. package/app/modules/vgdynamictable/js/editable.js +0 -438
  64. package/app/modules/vgdynamictable/js/expandable.js +0 -248
  65. package/app/modules/vgdynamictable/js/filters.js +0 -450
  66. package/app/modules/vgdynamictable/js/fixed.js +0 -566
  67. package/app/modules/vgdynamictable/js/options.js +0 -650
  68. package/app/modules/vgdynamictable/js/pagination.js +0 -623
  69. package/app/modules/vgdynamictable/js/search.js +0 -82
  70. package/app/modules/vgdynamictable/js/skeleton.js +0 -144
  71. package/app/modules/vgdynamictable/js/sortable.js +0 -442
  72. package/app/modules/vgdynamictable/js/summary-footer.js +0 -284
  73. package/app/modules/vgdynamictable/js/table-remote.js +0 -821
  74. package/app/modules/vgdynamictable/js/table-state.js +0 -243
  75. package/app/modules/vgdynamictable/js/table-url-state.js +0 -444
  76. package/app/modules/vgdynamictable/js/utils/common.js +0 -48
  77. package/app/modules/vgdynamictable/js/vgdynamictable.js +0 -3870
  78. package/app/modules/vgdynamictable/js/viewport.js +0 -322
  79. package/app/modules/vgdynamictable/readme.md +0 -251
  80. package/app/modules/vgdynamictable/scss/_actions.scss +0 -196
  81. package/app/modules/vgdynamictable/scss/_pagination.scss +0 -186
  82. package/app/modules/vgdynamictable/scss/_skeleton.scss +0 -63
  83. package/app/modules/vgdynamictable/scss/_sortable.scss +0 -66
  84. package/app/modules/vgdynamictable/scss/_table.scss +0 -137
  85. package/app/modules/vgdynamictable/scss/_variables.scss +0 -138
  86. package/app/modules/vgdynamictable/scss/vgdynamictable.scss +0 -264
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # VEGAS APP (VGApp)
1
+ # VEGAS APP (VGApp)
2
2
  [FUNC.TRUE?!](http://func-true.ru)
3
3
 
4
4
  VGApp — набор фронтенд‑модулей (UI‑компонентов и утилит) на чистом JavaScript от Vegas Studio.
@@ -20,7 +20,6 @@ VGApp — набор фронтенд‑модулей (UI‑компонент
20
20
  - `VGAlert` — диалоги подтверждения и уведомления с поддержкой AJAX‑сценариев.
21
21
  - `VGCollapse` — сворачиваемые секции и аккордеоны с анимацией.
22
22
  - `VGDropdown` — выпадающие меню с гибким открытием и позиционированием.
23
- - `VGDynamicTable` — интерактивные таблицы с сортировкой, фильтрацией, remote-режимом и состоянием.
24
23
  - `VGFiles` — загрузка и управление файлами (dropzone, валидация, AJAX, сортировка).
25
24
  - `VGFilePreview` — предпросмотр файлов разных типов и действия по файлу (preview/download).
26
25
  - `VGFormSender` — отправка форм (AJAX/нативно), обработка состояний и ответов.
@@ -35,6 +34,7 @@ VGApp — набор фронтенд‑модулей (UI‑компонент
35
34
  - `VGSidebar` — боковые панели с управлением открытием/закрытием.
36
35
  - `VGSpy` — scroll‑spy для подсветки активных ссылок по секциям.
37
36
  - `VGTabs` — вкладки с переключением контента и поддержкой URL/hash.
37
+ - `VGTable` — базовый модуль таблиц.
38
38
  - `VGToast` — всплывающие уведомления со стеком и автозакрытием.
39
39
  - `VGTooltip` — tooltip/popover подсказки с Data API, автопозиционированием и закрытием по `Esc`/клику вне элемента.
40
40
 
@@ -268,3 +268,25 @@ CSS-переменные темы генерируются в `app/utils/scss/_t
268
268
 
269
269
  ## License
270
270
  Is published under the [MIT license](http://www.opensource.org/licenses/mit-license)
271
+
272
+ ## Проекты на VGApp
273
+
274
+ Используете VGApp в публичном сайте, внутреннем сервисе или open-source проекте? Расскажите об этом сообществу — создайте Pull Request или Issue и укажите:
275
+
276
+ - название проекта;
277
+ - ссылку, если проект публичный;
278
+ - используемые модули VGApp;
279
+ - короткое описание или отзыв;
280
+ - можно ли публиковать название, ссылку и логотип.
281
+
282
+ Для закрытого проекта ссылку раскрывать не обязательно: его можно указать без названия, например «Внутренняя CRM автосервиса».
283
+
284
+ ```md
285
+ | Проект | Сайт | Модули VGApp | Описание |
286
+ |---|---|---|---|
287
+ | Название проекта | https://example.com | VGModal, VGSelect | Короткое описание проекта |
288
+ ```
289
+
290
+ Список формируется добровольно. VGApp не использует скрытую телеметрию и не собирает адреса сайтов. Статистика скачиваний npm публикуется отдельно и не считается количеством проектов или сайтов.
291
+
292
+ Спасибо за интерес к VGApp, использование библиотеки в своих проектах, обратную связь и вклад в её развитие. Каждый новый проект помогает делать VGApp полезнее, надёжнее и удобнее для всего сообщества.
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Описание: базовый класс модулей VGApp с общими параметрами, жизненным циклом и сетевыми запросами.
3
+ * Возможности: хранит экземпляры, нормализует Data API, управляет событиями и выполняет AJAX-запросы.
4
+ */
1
5
  import {
2
6
  execute,
3
7
  executeAfterTransition,
@@ -54,7 +58,7 @@ class BaseModule {
54
58
  * - timeout = задержка перед отправкой, по умолчанию 0
55
59
  * @param callback
56
60
  */
57
- _route(callback) {
61
+ _route(callback, errorCallback) {
58
62
  if (typeof callback !== 'function') {
59
63
  return this._requestRoute(callback);
60
64
  }
@@ -121,13 +125,21 @@ class BaseModule {
121
125
  completeAjaxRequest(data, 'success')
122
126
  },
123
127
  onError: (err) => {
128
+ if ('loader' in this._params.ajax && this._params.ajax.loader) setData('');
129
+
130
+ if (typeof errorCallback === 'function') {
131
+ execute(errorCallback, [err]);
132
+ return;
133
+ }
134
+
124
135
  completeAjaxRequest(err, 'error')
125
136
  }
126
137
  }
127
138
 
128
139
  setTimeout(() => {
129
140
  if (this._params.ajax.method.toLowerCase() === 'get') {
130
- ajax.get(this._params.ajax.route, ajaxData);
141
+ const route = this._buildRouteUrl(this._params.ajax.data, this._params.ajax.route);
142
+ ajax.get(route.toString(), ajaxData);
131
143
  }
132
144
 
133
145
  if (this._params.ajax.method.toLowerCase() === 'post') {
@@ -1,12 +1,12 @@
1
+ /**
2
+ * Описание: общие SVG-ресурсы и вспомогательные функции Data API модулей VGApp.
3
+ * Возможности: выдаёт системные и файловые иконки и подключает обработчики dismiss.
4
+ */
1
5
  import EventHandler from "../utils/js/dom/event";
2
6
  import {isDisabled, isEmptyObj} from "../utils/js/functions";
3
7
  import Selectors from "../utils/js/dom/selectors";
4
8
  import {getSVG as resolveSVGByFile} from "../utils/js/components/file-icon";
5
9
 
6
- /**
7
- * Тут собраны вспомогательные скрипты для работы модулей
8
- */
9
-
10
10
  /**
11
11
  * Набор svg элементов
12
12
  * @param name
@@ -24,6 +24,7 @@ const getSVG = (name) => {
24
24
  warning: '<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M36.5 5C44.8543 5 52.8665 8.31874 58.7739 14.2261C64.6813 20.1335 68 28.1457 68 36.5C68 44.8543 64.6813 52.8665 58.7739 58.7739C52.8665 64.6813 44.8543 68 36.5 68C28.1457 68 20.1335 64.6813 14.2261 58.7739C8.31874 52.8665 5 44.8543 5 36.5C5 28.1457 8.31874 20.1335 14.2261 14.2261C20.1335 8.31874 28.1457 5 36.5 5ZM36.5 18.5C35.9287 18.4995 35.3635 18.6181 34.8406 18.8484C34.3178 19.0786 33.8487 19.4154 33.4633 19.8372C33.0779 20.259 32.7848 20.7565 32.6025 21.298C32.4203 21.8395 32.353 22.413 32.405 22.982L34.0475 41.009C34.1112 41.6151 34.397 42.1761 34.8499 42.5839C35.3028 42.9917 35.8906 43.2173 36.5 43.2173C37.1094 43.2173 37.6972 42.9917 38.1501 42.5839C38.603 42.1761 38.8888 41.6151 38.9525 41.009L40.5905 22.982C40.6424 22.4134 40.5753 21.8403 40.3933 21.2991C40.2114 20.7579 39.9186 20.2606 39.5337 19.8389C39.1488 19.4171 38.6803 19.0803 38.1579 18.8498C37.6356 18.6192 37.0709 18.5001 36.5 18.5ZM36.5 54.5C37.4548 54.5 38.3705 54.1207 39.0456 53.4456C39.7207 52.7705 40.1 51.8548 40.1 50.9C40.1 49.9452 39.7207 49.0295 39.0456 48.3544C38.3705 47.6793 37.4548 47.3 36.5 47.3C35.5452 47.3 34.6295 47.6793 33.9544 48.3544C33.2793 49.0295 32.9 49.9452 32.9 50.9C32.9 51.8548 33.2793 52.7705 33.9544 53.4456C34.6295 54.1207 35.5452 54.5 36.5 54.5Z" fill="#000000"/></svg>',
25
25
  info: '<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M36 4.5C18.6047 4.5 4.5 18.6047 4.5 36C4.5 53.3953 18.6047 67.5 36 67.5C53.3953 67.5 67.5 53.3953 67.5 36C67.5 18.6047 53.3953 4.5 36 4.5ZM33.75 20.8125C33.75 20.5031 34.0031 20.25 34.3125 20.25H37.6875C37.9969 20.25 38.25 20.5031 38.25 20.8125V39.9375C38.25 40.2469 37.9969 40.5 37.6875 40.5H34.3125C34.0031 40.5 33.75 40.2469 33.75 39.9375V20.8125ZM36 51.75C35.1168 51.732 34.2759 51.3685 33.6577 50.7375C33.0395 50.1065 32.6932 49.2584 32.6932 48.375C32.6932 47.4916 33.0395 46.6435 33.6577 46.0125C34.2759 45.3815 35.1168 45.018 36 45C36.8832 45.018 37.7241 45.3815 38.3423 46.0125C38.9605 46.6435 39.3068 47.4916 39.3068 48.375C39.3068 49.2584 38.9605 50.1065 38.3423 50.7375C37.7241 51.3685 36.8832 51.732 36 51.75Z" fill="#000000"/></svg>',
26
26
  dots: '<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 17C11.1046 17 12 16.1046 12 15C12 13.8954 11.1046 13 10 13C8.89543 13 8 13.8954 8 15C8 16.1046 8.89543 17 10 17Z" fill="black"/><path d="M10 12C11.1046 12 12 11.1046 12 10C12 8.89543 11.1046 8 10 8C8.89543 8 8 8.89543 8 10C8 11.1046 8.89543 12 10 12Z" fill="black"/><path d="M10 7C11.1046 7 12 6.10457 12 5C12 3.89543 11.1046 3 10 3C8.89543 3 8 3.89543 8 5C8 6.10457 8.89543 7 10 7Z" fill="black"/></svg>',
27
+ remove: '<svg class="vg-icon-remove" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M7.05 5.636 12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414L12 13.414l-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636Z" fill="currentColor"/></svg>',
27
28
  cross: '<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15.3149 0.340332C15.4478 0.20752 15.6027 0.118978 15.7798 0.074707C15.9569 0.0249023 16.134 0.0249023 16.311 0.074707C16.4881 0.118978 16.6431 0.20752 16.7759 0.340332C16.9142 0.473145 17.0055 0.630859 17.0498 0.813477C17.0941 0.99056 17.0941 1.16764 17.0498 1.34473C17.0055 1.52181 16.917 1.67676 16.7842 1.80957L1.80127 16.8008C1.67399 16.9281 1.52181 17.0111 1.34473 17.0498C1.16764 17.0941 0.987793 17.0941 0.805176 17.0498C0.628092 17.0111 0.470378 16.9253 0.332031 16.7925C0.199219 16.6597 0.110677 16.502 0.0664062 16.3193C0.0221354 16.1423 0.0221354 15.9652 0.0664062 15.7881C0.110677 15.611 0.196452 15.4588 0.32373 15.3315L15.3149 0.340332ZM16.7842 15.3232C16.917 15.4561 17.0028 15.6082 17.0415 15.7798C17.0858 15.9569 17.0858 16.134 17.0415 16.311C17.0028 16.4937 16.9142 16.6541 16.7759 16.7925C16.6431 16.9253 16.4881 17.0138 16.311 17.0581C16.134 17.1024 15.9569 17.1024 15.7798 17.0581C15.6027 17.0138 15.4478 16.9253 15.3149 16.7925L0.32373 1.80127C0.201986 1.67952 0.116211 1.53011 0.0664062 1.35303C0.0221354 1.17594 0.0221354 0.996094 0.0664062 0.813477C0.116211 0.630859 0.204753 0.473145 0.332031 0.340332C0.464844 0.20752 0.622559 0.118978 0.805176 0.074707C0.987793 0.0304362 1.16488 0.0304362 1.33643 0.074707C1.51351 0.118978 1.66846 0.204753 1.80127 0.332031L16.7842 15.3232Z" fill="#000"/></svg>',
28
29
  'eye-open': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M288 80c-65.2 0-118.8 29.6-159.9 67.7C89.6 183.5 63 226 49.4 256c13.6 30 40.2 72.5 78.6 108.3C169.2 402.4 222.8 432 288 432s118.8-29.6 159.9-67.7C486.4 328.5 513 286 526.6 256c-13.6-30-40.2-72.5-78.6-108.3C406.8 109.6 353.2 80 288 80zM95.4 112.6C142.5 68.8 207.2 32 288 32s145.5 36.8 192.6 80.6c46.8 43.5 78.1 95.4 93 131.1c3.3 7.9 3.3 16.7 0 24.6c-14.9 35.7-46.2 87.7-93 131.1C433.5 443.2 368.8 480 288 480s-145.5-36.8-192.6-80.6C48.6 356 17.3 304 2.5 268.3c-3.3-7.9-3.3-16.7 0-24.6C17.3 208 48.6 156 95.4 112.6zM288 336c44.2 0 80-35.8 80-80s-35.8-80-80-80c-.7 0-1.3 0-2 0c1.3 5.1 2 10.5 2 16c0 35.3-28.7 64-64 64c-5.5 0-10.9-.7-16-2c0 .7 0 1.3 0 2c0 44.2 35.8 80 80 80zm0-208a128 128 0 1 1 0 256 128 128 0 1 1 0-256z"/></svg>',
29
30
  'eye-hide': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zm151 118.3C226 97.7 269.5 80 320 80c65.2 0 118.8 29.6 159.9 67.7C518.4 183.5 545 226 558.6 256c-12.6 28-36.6 66.8-70.9 100.9l-53.8-42.2c9.1-17.6 14.2-37.5 14.2-58.7c0-70.7-57.3-128-128-128c-32.2 0-61.7 11.9-84.2 31.5l-46.1-36.1zM394.9 284.2l-81.5-63.9c4.2-8.5 6.6-18.2 6.6-28.3c0-5.5-.7-10.9-2-16c.7 0 1.3 0 2 0c44.2 0 80 35.8 80 80c0 9.9-1.8 19.4-5.1 28.2zm9.4 130.3C378.8 425.4 350.7 432 320 432c-65.2 0-118.8-29.6-159.9-67.7C121.6 328.5 95 286 81.4 256c8.3-18.4 21.5-41.5 39.4-64.8L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5l-41.9-33zM192 256c0 70.7 57.3 128 128 128c13.3 0 26.1-2 38.2-5.8L302 334c-23.5-5.4-43.1-21.2-53.7-42.3l-56.1-44.2c-.2 2.8-.3 5.6-.3 8.5z"/></svg>',
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Описание: компонент подтверждений и информационных сообщений VGAlert.
3
+ * Возможности: modal, overlay и dropdown-рендер, компактный размер, Data API, AJAX и Promise-сценарии.
4
+ */
1
5
  import BaseModule from "../../base-module";
2
6
  import VGModal from "../../vgmodal";
3
7
  import VGDropdown from "../../vgdropdown";
@@ -18,6 +22,7 @@ const CLASS_NAME_ALERT = "vg-alert";
18
22
  const CLASS_NAME_SHOW = "show";
19
23
  const CLASS_NAME_FADE = "fade";
20
24
  const CLASS_NAME_CLOSING = "closing";
25
+ const CLASS_NAME_COMPACT = `${CLASS_NAME_ALERT}-compact`;
21
26
  const DATA_AGREE = "data-vg-alert-agree";
22
27
  const DATA_CANCEL = "data-vg-alert-cancel";
23
28
 
@@ -31,6 +36,7 @@ class VGAlert {
31
36
  constructor(params = {}, lang = 'ru') {
32
37
  this.lang = lang;
33
38
  this._defaultParams = {
39
+ size: "default", // default or compact
34
40
  render: {
35
41
  type: "modal", // modal, overlay or dropdown
36
42
  dismiss: false,
@@ -349,6 +355,7 @@ class VGAlert {
349
355
 
350
356
  _setParams(params) {
351
357
  const merged = mergeDeepObject(this._defaultParams, params);
358
+ merged.size = merged.size === "compact" ? "compact" : "default";
352
359
  merged.buttons = mergeDeepObject(this._elementsDefault.buttons, merged.buttons);
353
360
  merged.message = mergeDeepObject(this._elementsDefault.message, merged.message);
354
361
  merged.icon = this._elementsDefault.icons[merged.theme];
@@ -356,11 +363,20 @@ class VGAlert {
356
363
  return merged;
357
364
  }
358
365
 
366
+ _applySizeClass(element) {
367
+ if (this._params.size === "compact") {
368
+ element?.classList.add(CLASS_NAME_COMPACT);
369
+ }
370
+
371
+ return element;
372
+ }
373
+
359
374
  _buildModal() {
360
375
  const id = `${CLASS_NAME_ALERT}-${makeRandomString()}`;
361
376
 
362
377
  return VGModal.build(id, this._params.modal, (modal) => {
363
378
  modal._element.classList.add(`${CLASS_NAME_ALERT}-modal`);
379
+ this._applySizeClass(modal._element);
364
380
  const body = Selectors.find(".vg-modal-body", modal._element);
365
381
  body.append(this._buildContent());
366
382
  });
@@ -391,8 +407,10 @@ class VGAlert {
391
407
  this._createButton(buttons, "cancel");
392
408
  }
393
409
 
410
+ const sizeClass = this._params.size === "compact" ? ` ${CLASS_NAME_COMPACT}` : "";
411
+
394
412
  return html.div(
395
- {class: `${CLASS_NAME_ALERT}-wrapper ${CLASS_NAME_ALERT}-${this._params.theme}`},
413
+ {class: `${CLASS_NAME_ALERT}-wrapper ${CLASS_NAME_ALERT}-${this._params.theme}${sizeClass}`},
396
414
  [
397
415
  html.div(
398
416
  {class: `${CLASS_NAME_ALERT}-content`},
@@ -438,6 +456,7 @@ class VGAlert {
438
456
  const overlay = document.createElement('div');
439
457
 
440
458
  overlay.className = `${CLASS_NAME_ALERT}-overlay`;
459
+ this._applySizeClass(overlay);
441
460
  overlay.append(this._buildContent());
442
461
 
443
462
  container.append(overlay);
@@ -533,6 +552,7 @@ class VGAlert {
533
552
 
534
553
  container.id = id;
535
554
  container.className = `vg-dropdown-content ${CLASS_NAME_ALERT}-dropdown`;
555
+ this._applySizeClass(container);
536
556
  container.setAttribute('tabindex', '-1');
537
557
  container.setAttribute('role', 'dialog');
538
558
  container._vgAlertDropdownParent = parent;
@@ -154,3 +154,51 @@
154
154
  }
155
155
  }
156
156
 
157
+ .vg-alert-modal.vg-alert-compact {
158
+ --vg-modal-width: 420px !important;
159
+ }
160
+
161
+ .vg-alert-overlay.vg-alert-compact {
162
+ .vg-alert-wrapper {
163
+ width: min(420px, calc(100% - 2rem));
164
+ }
165
+ }
166
+
167
+ .vg-alert-dropdown.vg-alert-compact {
168
+ width: 280px;
169
+ }
170
+
171
+ .vg-alert-wrapper.vg-alert-compact {
172
+ .vg-alert-content {
173
+ padding: .75rem;
174
+ gap: .75rem;
175
+
176
+ .vg-alert-content--title {
177
+ font-size: 1rem;
178
+ line-height: 1.25;
179
+ }
180
+
181
+ .vg-alert-content--description {
182
+ font-size: .8125rem;
183
+ line-height: 1.35;
184
+ }
185
+
186
+ .vg-alert-content--icon {
187
+ svg {
188
+ width: 24px;
189
+ height: 24px;
190
+ }
191
+ }
192
+ }
193
+
194
+ .vg-alert-buttons {
195
+ padding: .5rem .75rem;
196
+
197
+ .btn {
198
+ padding: .375rem .75rem;
199
+ font-size: .8125rem;
200
+ line-height: 1.25;
201
+ }
202
+ }
203
+ }
204
+
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Описание: выпадающее меню VGDropdown с управлением мышью и клавиатурой.
3
+ * Возможности: автоматическое размещение, hover/click режимы, outside-click, анимация и AJAX-контент.
4
+ */
1
5
  import BaseModule from "../../base-module";
2
6
  import EventHandler from "../../../utils/js/dom/event";
3
7
  import Selectors from "../../../utils/js/dom/selectors";
@@ -45,6 +49,8 @@ const EVENT_CLICK_DATA_API = `click.${NAME_KEY}.data.api`;
45
49
  const EVENT_MOUSEOVER_DATA_API = `mouseover.${NAME_KEY}.data.api`;
46
50
  const EVENT_MOUSEOUT_DATA_API = `mouseout.${NAME_KEY}.data.api`;
47
51
 
52
+ let isDocumentDataApiInitialized = false;
53
+
48
54
  /**
49
55
  * Компонент выпадающего списка (Dropdown).
50
56
  *
@@ -179,6 +185,7 @@ class VGDropdown extends BaseModule {
179
185
  this._setPlacement();
180
186
 
181
187
  const completeCallback = () => {
188
+ if (!this._element || !this._drop || !this._params) return;
182
189
  this._route((status, data) => {
183
190
  EventHandler.trigger(this._element, EVENT_KEY_LOADED, { stats: status, data });
184
191
  });
@@ -210,6 +217,13 @@ class VGDropdown extends BaseModule {
210
217
  * @return {void}
211
218
  */
212
219
  dispose() {
220
+ if (this._element) {
221
+ this._element.classList.remove(CLASS_NAME_SHOW);
222
+ this._element.setAttribute('aria-expanded', 'false');
223
+ }
224
+ if (this._drop) {
225
+ this._drop.classList.remove(CLASS_NAME_SHOW, CLASS_NAME_FADE, CLASS_NAME_OPEN);
226
+ }
213
227
  super.dispose();
214
228
  }
215
229
 
@@ -247,7 +261,9 @@ class VGDropdown extends BaseModule {
247
261
  }
248
262
 
249
263
  setTimeout(() => {
264
+ if (!this._element || !this._drop || !this._params) return;
250
265
  const completeCallback = () => {
266
+ if (!this._element || !this._drop) return;
251
267
  this._drop.classList.remove(CLASS_NAME_SHOW);
252
268
  EventHandler.trigger(this._drop, EVENT_KEY_HIDDEN, relatedTarget);
253
269
  };
@@ -262,30 +278,28 @@ class VGDropdown extends BaseModule {
262
278
  _setPlacement() {
263
279
  if (!this._drop) return;
264
280
 
265
- if (!this._isPlacement) {
266
- let placementDefault = 'bottom-start',
267
- autoFlip = false,
268
- overflowProtection = false;
281
+ let placementDefault = 'bottom-start',
282
+ autoFlip = false,
283
+ overflowProtection = false;
269
284
 
270
- if (this._params.placement === 'auto') {
271
- autoFlip = true;
272
- overflowProtection = true;
273
- } else {
274
- placementDefault = this._params.placement
275
- }
285
+ if (this._params.placement === 'auto') {
286
+ autoFlip = true;
287
+ overflowProtection = true;
288
+ } else {
289
+ placementDefault = this._params.placement
290
+ }
276
291
 
277
- const placement = new Placement({
278
- reference: this._element,
279
- drop: this._drop,
280
- placement: placementDefault,
281
- boundary: 'clippingParents',
282
- autoFlip: autoFlip,
283
- overflowProtection: overflowProtection,
284
- fallbackPlacements: ['top-start', 'bottom-end', 'top-end'],
285
- });
292
+ const placement = new Placement({
293
+ reference: this._element,
294
+ drop: this._drop,
295
+ placement: placementDefault,
296
+ boundary: 'clippingParents',
297
+ autoFlip: autoFlip,
298
+ overflowProtection: overflowProtection,
299
+ fallbackPlacements: ['bottom-end', 'top-start', 'top-end', 'right-start', 'left-start', 'right-end', 'left-end'],
300
+ });
286
301
 
287
- placement._setPlacement(); // позиционируем
288
- }
302
+ placement._setPlacement(); // позиционируем при каждом открытии
289
303
 
290
304
  this._isPlacement = true;
291
305
  }
@@ -298,6 +312,9 @@ class VGDropdown extends BaseModule {
298
312
  */
299
313
  static init(element, params = {}) {
300
314
  const instance = VGDropdown.getOrCreateInstance(element, params);
315
+ if (instance._isDataApiInitialized) return instance;
316
+
317
+ instance._isDataApiInitialized = true;
301
318
 
302
319
  if (instance._params.hover && !isMobileDevice()) {
303
320
  let currentElem = null;
@@ -329,11 +346,14 @@ class VGDropdown extends BaseModule {
329
346
  });
330
347
  }
331
348
 
332
- // Клавиатурные события
333
- EventHandler.on(document, EVENT_KEYUP_DATA_API, SELECTOR_DATA_TOGGLE, VGDropdown.keydownHandler);
334
- EventHandler.on(document, EVENT_KEYDOWN_DATA_API, `.${TARGET_CONTAINER}`, VGDropdown.keydownHandler);
335
- EventHandler.on(document, EVENT_KEYUP_DATA_API, VGDropdown.clearDrops);
336
- EventHandler.on(document, EVENT_CLICK_DATA_API, VGDropdown.clearDrops);
349
+ if (!isDocumentDataApiInitialized) {
350
+ // Клавиатура и outside-click подключаются один раз для всех экземпляров.
351
+ EventHandler.on(document, EVENT_KEYUP_DATA_API, SELECTOR_DATA_TOGGLE, VGDropdown.keydownHandler);
352
+ EventHandler.on(document, EVENT_KEYDOWN_DATA_API, `.${TARGET_CONTAINER}`, VGDropdown.keydownHandler);
353
+ EventHandler.on(document, EVENT_KEYUP_DATA_API, VGDropdown.clearDrops);
354
+ EventHandler.on(document, EVENT_CLICK_DATA_API, VGDropdown.clearDrops);
355
+ isDocumentDataApiInitialized = true;
356
+ }
337
357
 
338
358
  // Клик по тоглу
339
359
  EventHandler.on(element, EVENT_CLICK_DATA_API, (event) => {
@@ -344,6 +364,17 @@ class VGDropdown extends BaseModule {
344
364
  return instance;
345
365
  }
346
366
 
367
+ /**
368
+ * Инициализирует все Dropdown-триггеры в документе.
369
+ * Повторный вызов безопасен и возвращает уже созданные экземпляры.
370
+ * @param {Object} [params] - Общие параметры для найденных элементов.
371
+ * @return {VGDropdown[]} Экземпляры найденных Dropdown.
372
+ */
373
+ static initAll(params = {}) {
374
+ return Selectors.findAll(SELECTOR_DATA_TOGGLE)
375
+ .map(element => VGDropdown.init(element, params));
376
+ }
377
+
347
378
  /**
348
379
  * Скрывает все открытые выпадающие списки.
349
380
  * @param {Event} event - Событие, инициировавшее скрытие.
@@ -419,4 +450,4 @@ class VGDropdown extends BaseModule {
419
450
  }
420
451
  }
421
452
 
422
- export default VGDropdown;
453
+ export default VGDropdown;
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Описание: модальное окно видеопредпросмотра VGFilePreview.
3
+ * Возможности: воспроизводит видео и переключает соседние записи плейлиста кнопками и горячими клавишами.
4
+ */
1
5
  import VGModal from "../../../vgmodal";
2
6
 
3
7
  class VideoModal {
@@ -139,7 +143,7 @@ class VideoModal {
139
143
  this._video.addEventListener('ended', () => this._goNext(true));
140
144
 
141
145
  header.appendChild(this._title);
142
- //header.appendChild(navigation);
146
+ header.appendChild(navigation);
143
147
  body.appendChild(this._video);
144
148
 
145
149
  this._bindEvents(element);
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Описание: рендерер видеофайлов для VGFilePreview.
3
+ * Возможности: создаёт действие предпросмотра и собирает актуальный плейлист соседних видео при открытии.
4
+ */
1
5
  import VideoModal from "./video-modal";
2
6
  import { buildMediaPlaylist } from "./playlist";
3
7
 
@@ -31,7 +35,6 @@ class VideoFilePreviewRenderer {
31
35
  return false;
32
36
  }
33
37
 
34
- const playlist = buildMediaPlaylist(context?.element, (ext) => VIDEO_EXTENSIONS.has(ext));
35
38
  const labels = {
36
39
  prev: i18n?.button('prev') || '',
37
40
  next: i18n?.button('next') || ''
@@ -42,6 +45,8 @@ class VideoFilePreviewRenderer {
42
45
  event.preventDefault();
43
46
  }
44
47
 
48
+ const playlist = buildMediaPlaylist(context?.element, (ext) => VIDEO_EXTENSIONS.has(ext));
49
+
45
50
  this._modal.open({
46
51
  src,
47
52
  title: context?.fileMeta?.name || i18n?.message('video_title') || '',
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Описание: основной контроллер предпросмотра файлов и синхронизации полей карточки.
3
+ * Возможности: валидирует путь, выбирает рендерер, управляет медиа, скачиванием, состояниями и публичным API.
4
+ */
1
5
  import BaseModule from "../../base-module";
2
6
  import {mergeDeepObject} from "../../../utils/js/functions";
3
7
  import FilePreviewHelper from "../../../utils/js/components/file-preview";
@@ -761,7 +765,7 @@ class VGFilePreview extends BaseModule {
761
765
  }
762
766
 
763
767
  if (this._isImageFile()) {
764
- return false;
768
+ return this._isPreviewGroupEnabled('image');
765
769
  }
766
770
 
767
771
  if (this._isOfficeFile()) {
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Описание: базовая логика выбора, валидации и отображения файлов VGFiles.
3
+ * Возможности: управляет набором файлов, ошибками, списками, кастомными действиями, статистикой и скрытыми полями формы.
4
+ */
1
5
  import BaseModule from "../../base-module";
2
6
  import {mergeDeepObject} from "../../../utils/js/functions";
3
7
  import Html from "../../../utils/js/components/templater";
@@ -140,14 +144,14 @@ class VGFilesBase extends BaseModule {
140
144
  Boolean(this._params?.replace) &&
141
145
  isSingle;
142
146
 
143
- if (shouldReplaceOnSingle) {
144
- this.clear(false);
145
- this.append(filesArray, true);
146
- this._revokeUrls();
147
- this._cleanupFakeInputs();
148
- if (this._params.prepend) this._files.reverse();
149
-
150
- this.build();
147
+ if (shouldReplaceOnSingle) {
148
+ this.clear(false);
149
+ this.append(filesArray, true);
150
+ this._revokeUrls();
151
+ this._cleanupFakeInputs();
152
+ if (this._params.prepend) this._files.reverse();
153
+
154
+ this.build();
151
155
  return;
152
156
  }
153
157
 
@@ -418,7 +422,7 @@ class VGFilesBase extends BaseModule {
418
422
  };
419
423
  }
420
424
 
421
- _renderUIDropList(files) {
425
+ _renderUIDropList(files) {
422
426
  if (!this._nodes.drop) return;
423
427
 
424
428
  let $list = Selectors.find(`.${this._getClass('drop-list')}`, this._nodes.drop);
@@ -431,9 +435,10 @@ class VGFilesBase extends BaseModule {
431
435
  const fragment = document.createDocumentFragment();
432
436
 
433
437
  files.forEach((file) => {
434
- let classes = $itemsTemplateClasses;
438
+ let classes = [...$itemsTemplateClasses];
435
439
 
436
- if (this._params.detach) classes.push('with-remove')
440
+ if (this._params.detach) classes.push('with-remove')
441
+ if (this._params.customButtons?.hideOnUploadState) classes.push('hide-custom-buttons-on-upload-state')
437
442
  if (this._params.sortable.enabled) classes.push('with-sortable')
438
443
  if (this._params.limits.count === 1) {
439
444
  classes.push('single');
@@ -547,7 +552,11 @@ class VGFilesBase extends BaseModule {
547
552
 
548
553
  this._initFilePreviewInInfo($list);
549
554
 
550
- Classes.add(this._nodes.info, 'show')
555
+ if (files.length) {
556
+ Classes.add(this._nodes.info, 'show');
557
+ } else {
558
+ Classes.remove(this._nodes.info, 'show');
559
+ }
551
560
  }
552
561
 
553
562
  _getInfoItemSignature(file, index) {
@@ -598,12 +607,13 @@ class VGFilesBase extends BaseModule {
598
607
  return '';
599
608
  }
600
609
 
601
- _createInfoListItem(file, i, itemsTemplate, itemTemplateClasses, fileKey, signature) {
610
+ _createInfoListItem(file, i, itemsTemplate, itemTemplateClasses, fileKey, signature) {
602
611
  let classes = [...itemTemplateClasses];
603
612
 
604
613
  if (this._params.image) classes.push('with-image');
605
614
  if (this._params.info) classes.push('with-info');
606
- if (this._params.detach) classes.push('with-remove')
615
+ if (this._params.detach) classes.push('with-remove')
616
+ if (this._params.customButtons?.hideOnUploadState) classes.push('hide-custom-buttons-on-upload-state')
607
617
  if (this._params.sortable.enabled) classes.push('with-sortable');
608
618
  const previewPath = this._resolveFilePreviewPath(file);
609
619
  const displayName = this._resolveDisplayName(file);
@@ -898,7 +908,11 @@ class VGFilesBase extends BaseModule {
898
908
  $count.innerHTML = this._files.length ? `${this._files.length}<span>[${totalSize}]</span>` : '';
899
909
  }
900
910
 
901
- Classes.add(this._nodes.stat, 'show');
911
+ if (this._files.length) {
912
+ Classes.add(this._nodes.stat, 'show');
913
+ } else {
914
+ Classes.remove(this._nodes.stat, 'show');
915
+ }
902
916
  }
903
917
 
904
918
  _generateHiddenInputs(files) {