vgapp 0.7.8 → 0.8.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 (52) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +22 -0
  3. package/app/langs/en/buttons.json +10 -0
  4. package/app/langs/en/messages.json +32 -0
  5. package/app/langs/en/titles.json +6 -0
  6. package/app/langs/ru/buttons.json +10 -0
  7. package/app/langs/ru/messages.json +32 -0
  8. package/app/langs/ru/titles.json +6 -0
  9. package/app/modules/base-module.js +23 -2
  10. package/app/modules/module-fn.js +20 -9
  11. package/app/modules/vgalert/js/vgalert.js +362 -214
  12. package/app/modules/vgalert/readme.md +242 -0
  13. package/app/modules/vgcollapse/js/vgcollapse.js +216 -62
  14. package/app/modules/vgcollapse/readme.md +56 -0
  15. package/app/modules/vgcollapse/scss/_variables.scss +5 -0
  16. package/app/modules/vgcollapse/scss/vgcollapse.scss +41 -0
  17. package/app/modules/vgdropdown/js/vgdropdown.js +140 -38
  18. package/app/modules/vgdropdown/readme.md +225 -0
  19. package/app/modules/vgfiles/js/base.js +499 -0
  20. package/app/modules/vgfiles/js/droppable.js +159 -0
  21. package/app/modules/vgfiles/js/loader.js +389 -0
  22. package/app/modules/vgfiles/js/render.js +83 -0
  23. package/app/modules/vgfiles/js/sortable.js +155 -0
  24. package/app/modules/vgfiles/js/vgfiles.js +796 -280
  25. package/app/modules/vgfiles/readme.md +193 -0
  26. package/app/modules/vgfiles/scss/_animations.scss +18 -0
  27. package/app/modules/vgfiles/scss/_mixins.scss +73 -0
  28. package/app/modules/vgfiles/scss/_variables.scss +103 -26
  29. package/app/modules/vgfiles/scss/vgfiles.scss +573 -60
  30. package/app/modules/vgformsender/js/vgformsender.js +5 -1
  31. package/app/modules/vgformsender/readme.md +30 -1
  32. package/app/modules/vglawcookie/js/vglawcookie.js +96 -62
  33. package/app/modules/vglawcookie/readme.md +102 -0
  34. package/app/modules/vgsidebar/js/vgsidebar.js +6 -4
  35. package/app/utils/js/components/ajax.js +176 -104
  36. package/app/utils/js/components/animation.js +124 -39
  37. package/app/utils/js/components/backdrop.js +54 -31
  38. package/app/utils/js/components/lang.js +71 -64
  39. package/app/utils/js/components/params.js +34 -31
  40. package/app/utils/js/components/scrollbar.js +118 -67
  41. package/app/utils/js/components/templater.js +14 -4
  42. package/app/utils/js/dom/cookie.js +107 -64
  43. package/app/utils/js/dom/data.js +68 -20
  44. package/app/utils/js/dom/event.js +272 -239
  45. package/app/utils/js/dom/manipulator.js +135 -62
  46. package/app/utils/js/dom/selectors.js +134 -59
  47. package/app/utils/js/functions.js +183 -349
  48. package/build/vgapp.css +1 -1
  49. package/build/vgapp.css.map +1 -1
  50. package/index.scss +3 -0
  51. package/package.json +1 -1
  52. package/app/utils/js/components/overflow.js +0 -28
@@ -0,0 +1,56 @@
1
+ # VGCollapse
2
+
3
+ **VGCollapse** — это легковесный и гибкий JavaScript-модуль для создания анимированных блоков с возможностью сворачивания/разворачивания содержимого. Подходит для аккордеонов, скрытых форм, FAQ-секций и других интерактивных элементов, где нужно управлять видимостью контента.
4
+
5
+ ---
6
+
7
+ ## ✅ Возможности
8
+
9
+ - Плавная анимация открытия/закрытия (через `height` и `overflow`)
10
+ - Поддержка нескольких экземпляров на одной странице
11
+ - Работа без jQuery — чистый ES6+
12
+ - Возможность инициализации вручную или через data-атрибуты
13
+ - Совместимость с динамически добавленным контентом
14
+
15
+ ---
16
+
17
+ ## 📦 Установка
18
+
19
+ Подключите JS-файл в проект:
20
+
21
+ ```js
22
+ import VGCollapse from './path/to/vgcollapse.js';
23
+ ```
24
+
25
+ ---
26
+
27
+ ## 🧱 HTML Структура
28
+
29
+ ```html
30
+ <a href="#example-simple" data-vg-toggle="collapse" aria-expanded="true" class="btn btn-primary" data-hide-text="Скрыть контент" data-show-text="Показать контент">Скрыть контент</a>
31
+ <div class="vg-collapse show" id="example-simple">
32
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci eligendi magnam
33
+ minus nihil quaerat.
34
+ </div>
35
+ ```
36
+
37
+ ---
38
+
39
+ ## ⚙️ Инициализация
40
+
41
+ ### 1. Через data-атрибуты (авто-инициализация)
42
+
43
+ Добавьте `data-vg-collapse` — модуль автоматически найдёт и инициализирует все блоки.
44
+
45
+ ---
46
+
47
+ ## 📝 Заметки
48
+
49
+ - Контент должен быть видим при инициализации, чтобы корректно рассчитать высоту.
50
+ - Не используйте `display: none` на контейнере при инициализации.
51
+ - Поддерживает современные браузеры (включая Edge, не поддерживает IE11).
52
+
53
+ ---
54
+
55
+ > 🚀 Автор: VEGAS STUDIO (vegas-dev.com)
56
+ > 📍 Поддерживается в проектах VEGAS
@@ -0,0 +1,5 @@
1
+ $collapse-accordion-map: (
2
+ border: 1px solid $border-color,
3
+ border-radius: $border-radius,
4
+ padding: 1rem
5
+ );
@@ -0,0 +1,41 @@
1
+ /**
2
+ *--------------------------------------------------------------------------
3
+ * Модуль: VGCollapse, реализация vg-accordion
4
+ * Автор: Vegas DEV
5
+ * Лицензия: смотри LICENSE
6
+ *--------------------------------------------------------------------------
7
+ **/
8
+
9
+ @import "../../../utils/scss/functions";
10
+ @import "../../../utils/scss/mixin";
11
+ @import "../../../utils/scss/variables";
12
+ @import "variables";
13
+
14
+
15
+ .vg-accordion {
16
+ @include mix-vars('accordion', $collapse-accordion-map);
17
+ position: relative;
18
+ border: var(--vg-accordion-border);
19
+ border-radius: var(--vg-accordion-border-radius);
20
+
21
+ &-item {
22
+ border-top: 0;
23
+ border-left: 0;
24
+ border-right: 0;
25
+ border-bottom: var(--vg-accordion-border);
26
+
27
+ &:last-child {
28
+ border: none;
29
+ }
30
+ }
31
+
32
+ &-button {
33
+ display: block;
34
+ padding: var(--vg-accordion-padding);
35
+ }
36
+
37
+ &-content {
38
+ display: block;
39
+ padding: var(--vg-accordion-padding);
40
+ }
41
+ }
@@ -3,10 +3,19 @@ import EventHandler from "../../../utils/js/dom/event";
3
3
  import Selectors from "../../../utils/js/dom/selectors";
4
4
  import {isDisabled, mergeDeepObject, noop} from "../../../utils/js/functions";
5
5
  import Placement from "../../../utils/js/components/placement";
6
- import Overflow from "../../../utils/js/components/overflow";
7
- import Backdrop from "../../../utils/js/components/backdrop";
8
- import {dismissTrigger} from "../../module-fn";
9
6
 
7
+ /**
8
+ * Константы, используемые в модуле выпадающего списка.
9
+ * @type {Object}
10
+ * @property {string} NAME - Имя модуля.
11
+ * @property {string} NAME_KEY - Уникальный ключ модуля с префиксом.
12
+ * @property {string} CLASS_NAME_SHOW - CSS-класс для отображения элемента.
13
+ * @property {string} CLASS_NAME_FADE - CSS-класс для эффекта затухания.
14
+ * @property {string} CLASS_NAME_OPEN - CSS-класс для немигающего открытия.
15
+ * @property {string} TARGET_CONTAINER - Класс контейнера выпадающего меню.
16
+ * @property {string} PARENT_CONTAINER - Класс родительского контейнера.
17
+ * @property {string} SELECTOR_DATA_TOGGLE - Селектор элемента-переключателя.
18
+ */
10
19
  const NAME = 'dropdown';
11
20
  const NAME_KEY = 'vg.dropdown';
12
21
  const CLASS_NAME_SHOW = 'show';
@@ -16,26 +25,75 @@ const TARGET_CONTAINER = 'vg-dropdown-content';
16
25
  const PARENT_CONTAINER = 'vg-dropdown';
17
26
  const SELECTOR_DATA_TOGGLE = '[data-vg-toggle="dropdown"]';
18
27
 
28
+ /**
29
+ * События, генерируемые модулем.
30
+ * @type {Object}
31
+ */
19
32
  const EVENT_KEY_HIDE = `${NAME_KEY}.hide`;
20
33
  const EVENT_KEY_HIDDEN = `${NAME_KEY}.hidden`;
21
34
  const EVENT_KEY_SHOW = `${NAME_KEY}.show`;
22
35
  const EVENT_KEY_SHOWN = `${NAME_KEY}.shown`;
23
-
24
- const EVENT_KEYUP_DATA_API = `keyup.${NAME_KEY}.data.api`;
25
- const EVENT_KEYDOWN_DATA_API = `keydown.${NAME_KEY}.data.api`;
26
- const EVENT_CLICK_DATA_API = `click.${NAME_KEY}.data.api`;
36
+ const EVENT_KEY_LOADED = `${NAME_KEY}.loaded`;
37
+
38
+ /**
39
+ * Делегированные события на уровне документа.
40
+ * @type {Object}
41
+ */
42
+ const EVENT_KEYUP_DATA_API = `keyup.${NAME_KEY}.data.api`;
43
+ const EVENT_KEYDOWN_DATA_API = `keydown.${NAME_KEY}.data.api`;
44
+ const EVENT_CLICK_DATA_API = `click.${NAME_KEY}.data.api`;
27
45
  const EVENT_MOUSEOVER_DATA_API = `mouseover.${NAME_KEY}.data.api`;
28
- const EVENT_MOUSEOUT_DATA_API = `mouseout.${NAME_KEY}.data.api`;
29
-
46
+ const EVENT_MOUSEOUT_DATA_API = `mouseout.${NAME_KEY}.data.api`;
47
+
48
+ /**
49
+ * Компонент выпадающего списка (Dropdown).
50
+ *
51
+ * @extends BaseModule
52
+ *
53
+ * @example
54
+ * const dropdown = new VGDropdown(document.querySelector('[data-vg-toggle="dropdown"]'), {
55
+ * placement: 'bottom-start',
56
+ * hover: true,
57
+ * animation: {
58
+ * enable: true,
59
+ * in: 'animate__fadeIn',
60
+ * out: 'animate__fadeOut',
61
+ * delay: 150
62
+ * }
63
+ * });
64
+ *
65
+ * @example <caption>Инициализация через data-атрибуты</caption>
66
+ * <div class="vg-dropdown">
67
+ * <button data-vg-toggle="dropdown" aria-expanded="false">Меню</button>
68
+ * <div class="vg-dropdown-content">Содержимое меню</div>
69
+ * </div>
70
+ */
30
71
  class VGDropdown extends BaseModule {
72
+ /**
73
+ * Создаёт экземпляр VGDropdown.
74
+ *
75
+ * @param {HTMLElement} element - Элемент-переключатель (кнопка).
76
+ * @param {Object} [params] - Пользовательские параметры.
77
+ * @param {string} [params.placement='auto'] - Позиция выпадающего окна: 'top', 'bottom', 'left', 'right', 'auto' и т.д.
78
+ * @param {boolean} [params.hover=false] - Открывать по наведению мыши.
79
+ * @param {Object} [params.ajax] - Параметры AJAX-загрузки.
80
+ * @param {string} [params.ajax.route=''] - URL для загрузки контента.
81
+ * @param {string} [params.ajax.target=''] - Селектор внутри drop для вставки данных.
82
+ * @param {string} [params.ajax.method='get'] - HTTP-метод.
83
+ * @param {boolean} [params.ajax.loader=false] - Показывать ли лоадер.
84
+ * @param {boolean} [params.ajax.once=false] - Загружать один раз.
85
+ * @param {boolean} [params.ajax.output=true] - Вставлять ли ответ в DOM.
86
+ * @param {Object} [params.animation] - Настройки анимации.
87
+ * @param {boolean} [params.animation.fade=false] - Использовать fade-анимацию.
88
+ * @param {boolean} [params.animation.enable=false] - Включить CSS-анимации.
89
+ * @param {string} [params.animation.in='animate__flipInY'] - Класс для анимации входа.
90
+ * @param {string} [params.animation.out='animate__flipOutY'] - Класс для анимации выхода.
91
+ * @param {number} [params.animation.delay=300] - Задержка перед завершением скрытия (в мс).
92
+ */
31
93
  constructor(element, params) {
32
94
  super(element, params);
33
95
 
34
96
  let defaultParams = {
35
- backdrop: false,
36
- overflow: false,
37
- keyboard: false,
38
- timeoutAnimation: 10,
39
97
  placement: 'auto',
40
98
  hover: false,
41
99
  ajax: {
@@ -71,18 +129,36 @@ class VGDropdown extends BaseModule {
71
129
  this._animation(this._drop, VGDropdown.NAME_KEY, this._params.animation);
72
130
  }
73
131
 
132
+ /**
133
+ * Возвращает имя компонента.
134
+ * @return {string}
135
+ */
74
136
  static get NAME() {
75
137
  return NAME;
76
138
  }
77
139
 
140
+ /**
141
+ * Возвращает уникальный ключ компонента.
142
+ * @return {string}
143
+ */
78
144
  static get NAME_KEY() {
79
145
  return NAME_KEY;
80
146
  }
81
147
 
148
+ /**
149
+ * Переключает состояние выпадающего списка (открыто/закрыто).
150
+ * @return {void}
151
+ */
82
152
  toggle() {
83
153
  return this._isShown() ? this.hide() : this.show();
84
154
  }
85
155
 
156
+ /**
157
+ * Открывает выпадающий список.
158
+ * @fires VGDropdown#show - Перед открытием.
159
+ * @fires VGDropdown#shown - После открытия.
160
+ * @return {void}
161
+ */
86
162
  show() {
87
163
  if (isDisabled(this._element) || this._isShown()) return;
88
164
 
@@ -101,18 +177,12 @@ class VGDropdown extends BaseModule {
101
177
  this._element.classList.add(CLASS_NAME_SHOW);
102
178
  this._drop.classList.add(CLASS_NAME_SHOW);
103
179
  this._setPlacement();
104
- this._route();
105
-
106
- if (this._params.backdrop && !this._params.hover) {
107
- Backdrop.show();
108
- }
109
-
110
- if (this._params.overflow) {
111
- Overflow.append();
112
- document.body.classList.add('dropdown-open');
113
- }
114
180
 
115
181
  const completeCallback = () => {
182
+ this._route((status, data) => {
183
+ EventHandler.trigger(this._element, EVENT_KEY_LOADED, { stats: status, data });
184
+ });
185
+
116
186
  if (this.isFade) {
117
187
  this._drop.classList.add(CLASS_NAME_FADE);
118
188
  } else if (!this.isAnimation) {
@@ -124,19 +194,39 @@ class VGDropdown extends BaseModule {
124
194
  this._queueCallback(completeCallback, this._drop, this.isAnimation || this.isFade, 50);
125
195
  }
126
196
 
197
+ /**
198
+ * Закрывает выпадающий список.
199
+ * @fires VGDropdown#hide - Перед закрытием.
200
+ * @fires VGDropdown#hidden - После закрытия.
201
+ * @return {void}
202
+ */
127
203
  hide() {
128
204
  if (isDisabled(this._element) || !this._isShown()) return;
129
205
  this._completeHide({ relatedTarget: this._element });
130
206
  }
131
207
 
208
+ /**
209
+ * Удаляет инстанс компонента и очищает обработчики событий.
210
+ * @return {void}
211
+ */
132
212
  dispose() {
133
213
  super.dispose();
134
214
  }
135
215
 
216
+ /**
217
+ * Проверяет, открыто ли выпадающее меню.
218
+ * @return {boolean} - `true`, если открыто.
219
+ * @private
220
+ */
136
221
  _isShown() {
137
222
  return this._element.classList.contains(CLASS_NAME_SHOW);
138
223
  }
139
224
 
225
+ /**
226
+ * Полностью закрывает меню с анимацией и callback.
227
+ * @param {Object} relatedTarget - Событие-инициатор.
228
+ * @private
229
+ */
140
230
  _completeHide(relatedTarget) {
141
231
  const hideEvent = EventHandler.trigger(this._drop, EVENT_KEY_HIDE, relatedTarget);
142
232
  if (hideEvent.defaultPrevented) return;
@@ -156,19 +246,6 @@ class VGDropdown extends BaseModule {
156
246
  this._drop.classList.remove(CLASS_NAME_OPEN);
157
247
  }
158
248
 
159
- if (this._params.backdrop && !this._params.hover) {
160
- Backdrop.hide(() => {
161
- if (this._params.overflow) {
162
- Overflow.destroy();
163
- }
164
- });
165
- }
166
-
167
- if (this._params.overflow) {
168
- Overflow.destroy();
169
- document.body.classList.remove('dropdown-open');
170
- }
171
-
172
249
  setTimeout(() => {
173
250
  const completeCallback = () => {
174
251
  this._drop.classList.remove(CLASS_NAME_SHOW);
@@ -178,6 +255,10 @@ class VGDropdown extends BaseModule {
178
255
  }, this._params.animation.delay);
179
256
  }
180
257
 
258
+ /**
259
+ * Устанавливает позицию выпадающего окна с помощью вспомогательного класса Placement.
260
+ * @private
261
+ */
181
262
  _setPlacement() {
182
263
  if (!this._drop) return;
183
264
 
@@ -209,6 +290,12 @@ class VGDropdown extends BaseModule {
209
290
  this._isPlacement = true;
210
291
  }
211
292
 
293
+ /**
294
+ * Инициализирует компонент на указанном элементе и устанавливает обработчики событий.
295
+ * @param {HTMLElement} element - Элемент-переключатель.
296
+ * @param {Object} [params] - Параметры инициализации.
297
+ * @return {VGDropdown} - Экземпляр компонента.
298
+ */
212
299
  static init(element, params = {}) {
213
300
  const instance = VGDropdown.getOrCreateInstance(element, params);
214
301
 
@@ -253,8 +340,15 @@ class VGDropdown extends BaseModule {
253
340
  event.preventDefault();
254
341
  instance.toggle();
255
342
  });
343
+
344
+ return instance;
256
345
  }
257
346
 
347
+ /**
348
+ * Скрывает все открытые выпадающие списки.
349
+ * @param {Event} event - Событие, инициировавшее скрытие.
350
+ * @static
351
+ */
258
352
  static hideOpenToggles(event) {
259
353
  const openToggles = Selectors.findAll(`${SELECTOR_DATA_TOGGLE}:not(.disabled):not(:disabled).${CLASS_NAME_SHOW}`);
260
354
  for (const toggle of openToggles) {
@@ -279,6 +373,11 @@ class VGDropdown extends BaseModule {
279
373
  }
280
374
  }
281
375
 
376
+ /**
377
+ * Обработчик клавиатурных событий (стрелки, Esc).
378
+ * @param {KeyboardEvent} event - Клавиатурное событие.
379
+ * @static
380
+ */
282
381
  static keydownHandler(event) {
283
382
  const isInput = /input|textarea/i.test(event.target.tagName);
284
383
  const isEscapeEvent = event.key === 'Escape';
@@ -307,6 +406,11 @@ class VGDropdown extends BaseModule {
307
406
  }
308
407
  }
309
408
 
409
+ /**
410
+ * Обработчик кликов и Tab для закрытия выпадающих списков.
411
+ * @param {Event} event - Событие (click или keyup).
412
+ * @static
413
+ */
310
414
  static clearDrops(event) {
311
415
  if (event.button === 2 || (event.type === 'keyup' && event.key !== 'Tab')) {
312
416
  return;
@@ -315,6 +419,4 @@ class VGDropdown extends BaseModule {
315
419
  }
316
420
  }
317
421
 
318
- dismissTrigger(VGDropdown);
319
-
320
422
  export default VGDropdown;
@@ -0,0 +1,225 @@
1
+ # VGDropdown — Модуль выпадающего списка
2
+
3
+ **VGDropdown** — это универсальный и гибко настраиваемый JavaScript-компонент для создания интерактивных
4
+ выпадающих меню (dropdown) в веб-интерфейсах. Поддерживает позиционирование, анимации, AJAX-загрузку контента,
5
+ работу с hover/кликами, клавиатурную навигацию и доступность.
6
+
7
+ ---
8
+
9
+ ## 📌 Основные возможности
10
+
11
+ - Открытие/закрытие по клику или наведению (`hover`)
12
+ - Поддержка **автоматического позиционирования** (с учётом границ экрана и overflow)
13
+ - Полная **клавиатурная навигация** (стрелки, Esc, Tab)
14
+ - **AJAX-загрузка контента** динамически
15
+ - **CSS-анимации** (вход/выход) с использованием Animate.css
16
+ - Поддержка **fade-эффектов** и кастомных задержек
17
+ - Автоматическое закрытие других открытых dropdown'ов
18
+ - Полная **доступность (a11y)**: `aria-expanded`
19
+ - Совместимость с **мобильными устройствами**
20
+ - Гибкая инициализация: через JS или `data-*` атрибуты
21
+
22
+ ---
23
+
24
+ ## 🧩 Структура HTML
25
+
26
+ ```bladehtml
27
+ <div class="vg-dropdown">
28
+ <a href="#" data-vg-toggle="dropdown" data-params='{"hover": "true"}' ( или data-hover="true") class="btn btn-primary" aria-expanded="false">Open</a>
29
+ <div class="vg-dropdown-content">
30
+ <div class="vg-dropdown-container">
31
+ <ul class="list-group">
32
+ <li class="list-group-item"><a href="#">Home</a></li>
33
+ <li class="list-group-item"><a href="#">Services</a></li>
34
+ <li class="list-group-item"><a href="#">About</a></li>
35
+ <li class="list-group-item"><a href="#">Contacts</a></li>
36
+ </ul>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ ```
41
+
42
+ > 💡 Убедитесь, что `.vg-dropdown-content` находится внутри `.vg-dropdown`, либо укажите селектор явно через `data-vg-target`.
43
+
44
+ ---
45
+
46
+ ## ⚙️ Параметры инициализации
47
+
48
+ | Параметр | Тип | По умолчанию | Описание |
49
+ |--------|------|-------------|-------------------------------------------------------------------------------|
50
+ | `placement` | `string` | `'auto'` | Позиция меню: `'top-start'`, `'bottom-start'`, `'left-start'`, `'right-start'`, `'auto'` и т.д. |
51
+ | `hover` | `boolean` | `false` | Открывать при наведении мыши (на десктопе) |
52
+ | `animation.fade` | `boolean` | `true` | Добавить класс `fade` при показе |
53
+ | `animation.enable` | `boolean` | `false` | Включить CSS-анимации |
54
+ | `animation.in` | `string` | `'animate__flipInY'` | Класс анимации входа (например, Animate.css) |
55
+ | `animation.out` | `string` | `'animate__flipOutY'` | Класс анимации выхода |
56
+ | `animation.delay` | `number` | `300` | Задержка перед удалением `.show` (в мс) |
57
+ | `ajax.route` | `string` | `''` | URL для загрузки контента |
58
+ | `ajax.target` | `string` | `''` | Селектор внутри `.vg-dropdown-content` для вставки данных |
59
+ | `ajax.method` | `string` | `'get'` | HTTP-метод запроса |
60
+ | `ajax.loader` | `boolean` | `false` | Показывать лоадер при загрузке |
61
+ | `ajax.once` | `boolean` | `false` | Загружать контент только один раз |
62
+ | `ajax.output` | `boolean` | `true` | Вставлять ответ в DOM |
63
+
64
+ ---
65
+
66
+ ## 🚀 Инициализация
67
+
68
+ ### Через JavaScript
69
+ ```js
70
+ import VGDropdown from 'app/modules/vgdropdown/js/vgdropdown';
71
+
72
+ const dropdown = new VGDropdown(document.querySelector('[data-vg-toggle="dropdown"]'), {
73
+ placement: 'bottom-start',
74
+ hover: true,
75
+ animation: {
76
+ enable: true,
77
+ in: 'animate__fadeInDown',
78
+ out: 'animate__fadeOutUp',
79
+ delay: 200
80
+ },
81
+ ajax: {
82
+ route: '',
83
+ target: '',
84
+ method: 'get',
85
+ once: true
86
+ }
87
+ });
88
+ ```
89
+
90
+ > Атрибуты автоматически преобразуются в параметры:
91
+ > `data-placement` → `placement`
92
+ > `data-ajax-route` → `ajax.route`
93
+
94
+ ---
95
+
96
+ ## 🔁 Методы
97
+
98
+ | Метод | Описание |
99
+ |------|--------|
100
+ | `.toggle()` | Переключает состояние (открыть/закрыть) |
101
+ | `.show()` | Открывает меню |
102
+ | `.hide()` | Закрывает меню |
103
+ | `.dispose()` | Удаляет экземпляр и обработчики событий |
104
+ | `.getInstance(element)` | Получить существующий экземпляр |
105
+ | `.getOrCreateInstance(element, params)` | Получить или создать экземпляр |
106
+
107
+ ```js
108
+ VGDropdown.getOrCreateInstance().hide(); // Закрыть drop
109
+ ```
110
+ ---
111
+
112
+ ## 📣 События
113
+
114
+ VGDropdown генерирует пользовательские события.
115
+
116
+ | Событие | Срабатывает | Детали (`event.detail`) |
117
+ |--------|-------------|-------------------------|
118
+ | `vg.dropdown.show` | Перед открытием | `relatedTarget` — кнопка |
119
+ | `vg.dropdown.shown` | После открытия | `relatedTarget` |
120
+ | `vg.dropdown.hide` | Перед закрытием | `relatedTarget` |
121
+ | `vg.dropdown.hidden` | После закрытия | `relatedTarget` |
122
+ | `vg.dropdown.loaded` | После AJAX-загрузки | `stats`, `data` |
123
+
124
+ ```js
125
+ dropdown._drop.addEventListener('vg.dropdown.shown', (e) => {
126
+ console.log('Дроп открыт', e.detail.relatedTarget);
127
+ });
128
+ ```
129
+
130
+ ---
131
+
132
+ ## 🖱️ Поведение при наведении (`hover`)
133
+
134
+ ```js
135
+ hover: true
136
+ ```
137
+
138
+ - Работает только на десктопах (`!isMobileDevice()`)
139
+ - Автоматически закрывается при уходе курсора
140
+ - Не конфликтует с другими открытыми меню
141
+
142
+ ---
143
+
144
+ ## 🔁 AJAX-загрузка контента
145
+
146
+ ```js
147
+ ajax: {
148
+ route: '/menu',
149
+ target: '#drop-content', // Целевой селектор внутри .vg-dropdown-container
150
+ method: 'get',
151
+ once: true,
152
+ loader: false,
153
+ output: true
154
+ }
155
+ ```
156
+ - Контент загружается при первом открытии (если `once: true`)
157
+ - Ответ вставляется в указанный селектор
158
+ - Генерируется событие `vg.dropdown.loaded`
159
+
160
+ ---
161
+
162
+ ## 🎨 Анимации
163
+
164
+ Поддерживается два режима:
165
+
166
+ ### 1. Fade (CSS)
167
+ ### 2. CSS-анимации (через Animate.css)
168
+ ```js
169
+ animation: { enable: true, in: 'animate__fadeInUp', out: 'animate__fadeOutDown' }
170
+ ```
171
+ > Убедитесь, что подключили animate.css или свои классы анимаций.
172
+
173
+ ---
174
+
175
+ ## 📱 Адаптивность и мобильные устройства
176
+
177
+ - На мобильных устройствах `hover` отключается
178
+ - При открытии временно блокируются `mouseover` события на других элементах (для стабильности)
179
+ - Поддерживает тач-события корректно
180
+
181
+ ---
182
+
183
+ ## 🔐 Доступность (a11y)
184
+
185
+ - `aria-expanded` автоматически обновляется
186
+ - Поддержка клавиатуры: **Enter**, **Space**, **Esc**, **↑↓**
187
+ - Закрытие при клике вне или нажатии `Tab`
188
+
189
+ ---
190
+ ## 🧰 Статические методы
191
+
192
+ | Метод | Описание |
193
+ |------|--------|
194
+ | `VGDropdown.hideOpenToggles(event)` | Закрывает все открытые меню |
195
+ | `VGDropdown.keydownHandler(event)` | Обработчик клавиш |
196
+ | `VGDropdown.clearDrops(event)` | Обработчик кликов вне и Tab |
197
+
198
+ ---
199
+
200
+ ## 📦 CSS-классы
201
+
202
+ | Класс | Назначение |
203
+ |------|----------|
204
+ | `.vg-dropdown` | Родительский контейнер |
205
+ | `.vg-dropdown-content` | Выпадающее меню |
206
+ | `.show` | Показывает элемент |
207
+ | `.fade` | Эффект затухания |
208
+ | `.open` | Альтернатива fade (без анимации) |
209
+ | `[data-vg-toggle="dropdown"]` | Кнопка-переключатель |
210
+
211
+ ---
212
+
213
+ ## 📄 Лицензия
214
+
215
+ MIT — свободное использование и модификация.
216
+
217
+ ---
218
+
219
+ 🛠 Разработано с использованием современных паттернов и лучших практик.
220
+ Создано для масштабируемых, доступных и красивых интерфейсов.
221
+
222
+ ---
223
+
224
+ > 🚀 Автор: VEGAS STUDIO (vegas-dev.com)
225
+ > 📍 Поддерживается в проектах VEGAS