vgapp 1.2.4 → 1.2.6

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 (51) hide show
  1. package/CHANGELOG.md +66 -7
  2. package/README.md +2 -1
  3. package/agents.md +2 -2
  4. package/app/modules/base-module.js +87 -10
  5. package/app/modules/vgalert/js/vgalert.js +133 -202
  6. package/app/modules/vgalert/readme.md +105 -46
  7. package/app/modules/vgalert/scss/vgalert.scss +18 -0
  8. package/app/modules/vgdynamictable/index.js +5 -0
  9. package/app/modules/vgdynamictable/js/editable.js +438 -0
  10. package/app/modules/vgdynamictable/js/expandable.js +248 -0
  11. package/app/modules/vgdynamictable/js/filters.js +450 -0
  12. package/app/modules/vgdynamictable/js/fixed.js +566 -0
  13. package/app/modules/vgdynamictable/js/options.js +646 -0
  14. package/app/modules/vgdynamictable/js/pagination.js +623 -0
  15. package/app/modules/vgdynamictable/js/search.js +82 -0
  16. package/app/modules/vgdynamictable/js/skeleton.js +136 -0
  17. package/app/modules/vgdynamictable/js/sortable.js +442 -0
  18. package/app/modules/vgdynamictable/js/summary-footer.js +284 -0
  19. package/app/modules/vgdynamictable/js/table-remote.js +821 -0
  20. package/app/modules/vgdynamictable/js/table-state.js +243 -0
  21. package/app/modules/vgdynamictable/js/table-url-state.js +444 -0
  22. package/app/modules/vgdynamictable/js/utils/common.js +48 -0
  23. package/app/modules/vgdynamictable/js/vgdynamictable.js +3829 -0
  24. package/app/modules/vgdynamictable/js/viewport.js +322 -0
  25. package/app/modules/vgdynamictable/readme.md +251 -0
  26. package/app/modules/vgdynamictable/scss/_actions.scss +193 -0
  27. package/app/modules/vgdynamictable/scss/_pagination.scss +183 -0
  28. package/app/modules/vgdynamictable/scss/_skeleton.scss +60 -0
  29. package/app/modules/vgdynamictable/scss/_sortable.scss +63 -0
  30. package/app/modules/vgdynamictable/scss/_table.scss +157 -0
  31. package/app/modules/vgdynamictable/scss/_variables.scss +130 -0
  32. package/app/modules/vgdynamictable/scss/vgdynamictable.scss +267 -0
  33. package/app/modules/vgrangeslider/index.js +3 -0
  34. package/app/modules/vgrangeslider/js/skins.js +222 -0
  35. package/app/modules/vgrangeslider/js/vgrangeslider.js +704 -0
  36. package/app/modules/vgrangeslider/readme.md +523 -0
  37. package/app/modules/vgrangeslider/scss/_variables.scss +53 -0
  38. package/app/modules/vgrangeslider/scss/vgrangeslider.scss +240 -0
  39. package/app/modules/vgtoast/js/vgtoast.js +111 -111
  40. package/app/modules/vgtooltip/index.js +3 -0
  41. package/app/modules/vgtooltip/js/vgtooltip.js +493 -0
  42. package/app/modules/vgtooltip/readme.md +181 -0
  43. package/app/modules/vgtooltip/scss/_variables.scss +15 -0
  44. package/app/modules/vgtooltip/scss/vgtooltip.scss +64 -0
  45. package/app/utils/js/components/ajax.js +116 -7
  46. package/app/utils/js/components/placement.js +112 -41
  47. package/build/vgapp.css +1 -1
  48. package/build/vgapp.css.map +1 -1
  49. package/index.js +20 -17
  50. package/index.scss +9 -0
  51. package/package.json +1 -1
@@ -3,64 +3,12 @@ import VGModal from "../../vgmodal";
3
3
 
4
4
  import { isElement, isVisible, makeRandomString, mergeDeepObject } from "../../../utils/js/functions";
5
5
  import { getSVG } from "../../module-fn";
6
- import { Classes} from "../../../utils/js/dom/manipulator";
6
+ import {Classes, Manipulator} from "../../../utils/js/dom/manipulator";
7
7
  import Selectors from "../../../utils/js/dom/selectors";
8
8
  import EventHandler from "../../../utils/js/dom/event";
9
9
  import {lang_buttons, lang_messages} from "../../../utils/js/components/lang";
10
10
  import Html from "../../../utils/js/components/templater";
11
-
12
- /**
13
- * @typedef {Object} AjaxParams
14
- * @property {string} route - URL-адрес для AJAX-запроса.
15
- * @property {string} target - Селектор элемента, куда будет вставлен ответ.
16
- * @property {string} method - HTTP-метод ('get', 'post' и т.д.).
17
- * @property {boolean} loader - Показывать ли индикатор загрузки.
18
- * @property {boolean} once - Выполнить запрос только один раз.
19
- * @property {boolean} output - Выводить ли результат в целевой элемент.
20
- */
21
-
22
- /**
23
- * @typedef {Object} ModalParams
24
- * @property {boolean} centered - Центрировать ли модальное окно по вертикали.
25
- * @property {boolean|string} backdrop - Фоновая подложка ('static', true, false).
26
- * @property {boolean} overflow - Разрешить прокрутку фона при открытом окне.
27
- * @property {boolean} keyboard - Закрывать по нажатию Escape.
28
- * @property {boolean} dismiss - Закрывать при клике по подложке.
29
- * @property {Object} animation - Параметры анимации.
30
- * @property {boolean} animation.enable - Включить анимацию.
31
- * @property {string} animation.in - Класс анимации входа.
32
- * @property {string} animation.out - Класс анимации выхода.
33
- * @property {number} animation.delay - Задержка перед показом (мс).
34
- * @property {number} animation.duration - Длительность анимации (мс).
35
- */
36
-
37
- /**
38
- * @typedef {Object} ButtonConfig
39
- * @property {string} [element] - Готовый HTML-элемент кнопки.
40
- * @property {'button'|'a'} [tag='button'] - Тип элемента.
41
- * @property {string} [type='button'] - Атрибут type (для <button>).
42
- * @property {Object.<string, string>} [attr] - Дополнительные атрибуты.
43
- * @property {string} toggle - Атрибут данных для управления.
44
- * @property {string[]} class - CSS-классы кнопки.
45
- * @property {string} text - Текст кнопки.
46
- */
47
-
48
- /**
49
- * @typedef {Object} MessageConfig
50
- * @property {string} title - Заголовок сообщения.
51
- * @property {string} description - Описание/текст сообщения.
52
- */
53
-
54
- /**
55
- * @typedef {Object} AlertParams
56
- * @property {AjaxParams} ajax - Параметры AJAX-запроса.
57
- * @property {ModalParams} modal - Параметры модального окна.
58
- * @property {'confirm'|'info'} mode - Режим алерта: подтверждение или информационное.
59
- * @property {'danger'|'warning'|'success'|'info'} theme - Тема оформления.
60
- * @property {{agree: ButtonConfig, cancel: ButtonConfig}} buttons - Конфигурация кнопок.
61
- * @property {MessageConfig} message - Сообщение и заголовок.
62
- * @property {string} [icon] - SVG-иконка, соответствующая теме.
63
- */
11
+ import Params from "../../../utils/js/components/params";
64
12
 
65
13
  /**
66
14
  * Константы
@@ -75,38 +23,14 @@ const NAME_KEY = "vg.alert";
75
23
  // Глобальная блокировка: предотвращаем открытие нескольких алертов
76
24
  let isAlertOpen = false;
77
25
 
78
- /**
79
- * Класс VGAlert — модальное окно подтверждения или информационное уведомление.
80
- *
81
- * @class
82
- * @example
83
- * VGAlert.call({
84
- * mode: 'confirm',
85
- * theme: 'danger',
86
- * message: {
87
- * title: 'Вы уверены?',
88
- * description: 'Это действие нельзя отменить.'
89
- * },
90
- * buttons: {
91
- * agree: { text: 'Удалить', class: ['btn-danger'] },
92
- * cancel: { text: 'Отмена' }
93
- * }
94
- * }).then(() => {
95
- * console.log('Подтверждено');
96
- * }).catch(() => {
97
- * console.log('Отменено');
98
- * });
99
- */
100
26
  class VGAlert {
101
- /**
102
- * Создаёт экземпляр VGAlert.
103
- *
104
- * @param {AlertParams} params - Пользовательские параметры.
105
- * @param {'ru'|'en'} [lang='ru'] - Язык интерфейса.
106
- */
107
27
  constructor(params = {}, lang = 'ru') {
108
28
  this.lang = lang;
109
29
  this._defaultParams = {
30
+ render: {
31
+ type: "modal", // modal or overlay,
32
+ dismiss: false,
33
+ },
110
34
  ajax: {
111
35
  route: "",
112
36
  target: "",
@@ -169,29 +93,55 @@ class VGAlert {
169
93
  this._params = this._setParams(params);
170
94
  }
171
95
 
172
- /**
173
- * Открывает алерт и возвращает Promise.
174
- *
175
- * @static
176
- * @param {AlertParams} options - Параметры алерта.
177
- * @param {'ru'|'en'} [lang='ru'] - Язык.
178
- * @returns {Promise<{accepted: boolean, timestamp: Date}>} Результат взаимодействия.
179
- * @throws {Error} Если алерт уже открыт.
180
- */
181
96
  static call(options = {}, lang = 'ru') {
182
97
  const context = new VGAlert(options, lang);
183
98
 
184
99
  if (isAlertOpen) return Promise.reject({ accepted: false, reason: lang_messages(context.lang, NAME_KEY).reason });
185
100
  isAlertOpen = true;
186
101
 
187
- let modal = context._buildModal();
188
- modal.show();
102
+ const getContainer = () => {
103
+ if (context._params.render.type === "overlay") {
104
+ const overlay = context._buildOverlay();
105
+ return {
106
+ element: overlay.element,
107
+ render: overlay.render,
108
+ type: 'overlay'
109
+ }
110
+ } else if (context._params.render.type === "modal") {
111
+ const modal = context._buildModal();
112
+ return {
113
+ element: modal._element,
114
+ render: modal,
115
+ type: 'modal'
116
+ }
117
+ }
118
+ };
119
+
120
+ const {element: container, render, type} = getContainer();
121
+
122
+ if (type === 'modal') {
123
+ render.show();
124
+ }
189
125
 
190
- const container = modal._element;
191
126
  const agreeBtn = Selectors.find(`[${DATA_AGREE}]`, container);
192
127
  const cancelBtn = Selectors.find(`[${DATA_CANCEL}]`, container);
193
128
 
194
129
  return new Promise((resolve, reject) => {
130
+ const closeAlert = (isHide = true) => {
131
+ if (type === 'modal') {
132
+ render.hide();
133
+ return;
134
+ }
135
+
136
+ if (type === 'overlay') {
137
+ container.remove();
138
+
139
+ if (context._params.render.dismiss && isHide) {
140
+ render.hide();
141
+ }
142
+ }
143
+ };
144
+
195
145
  const handleAgree = (e) => {
196
146
  e.preventDefault();
197
147
  cleanup();
@@ -199,12 +149,17 @@ class VGAlert {
199
149
  accepted: true,
200
150
  timestamp: new Date(),
201
151
  });
202
- modal.hide();
152
+ closeAlert();
203
153
  };
204
154
 
205
155
  const handleCancel = (e) => {
206
156
  e.preventDefault();
207
- modal.hide();
157
+ cleanup();
158
+ reject({
159
+ accepted: false,
160
+ timestamp: new Date(),
161
+ });
162
+ closeAlert(false);
208
163
  };
209
164
 
210
165
  const handleKeydown = (e) => {
@@ -235,26 +190,21 @@ class VGAlert {
235
190
  }
236
191
 
237
192
  document.addEventListener("keydown", handleKeydown);
238
- container.addEventListener("vg.modal.hide", () => {
239
- cleanup();
240
- reject({
241
- accepted: false,
242
- timestamp: new Date(),
243
- });
244
- });
193
+
194
+ if (type === 'modal') {
195
+ container.addEventListener("vg.modal.hide", () => {
196
+ cleanup();
197
+ reject({
198
+ accepted: false,
199
+ timestamp: new Date(),
200
+ });
201
+ }, {once: true});
202
+ }
245
203
 
246
204
  container.focus();
247
205
  });
248
206
  }
249
207
 
250
- /**
251
- * Инициирует алерт подтверждения на основе DOM-элемента.
252
- *
253
- * @static
254
- * @param {HTMLElement} elem - Элемент, вызвавший алерт.
255
- * @param {AlertParams} options - Параметры алерта.
256
- * @returns {void}
257
- */
258
208
  static confirm(elem, options = {}) {
259
209
  let lang = 'ru';
260
210
 
@@ -269,13 +219,6 @@ class VGAlert {
269
219
  instance.run(VGAlert);
270
220
  }
271
221
 
272
- /**
273
- * Слияние пользовательских параметров с дефолтными.
274
- *
275
- * @private
276
- * @param {AlertParams} params - Пользовательские параметры.
277
- * @returns {AlertParams} Полный объект параметров.
278
- */
279
222
  _setParams(params) {
280
223
  const merged = mergeDeepObject(this._defaultParams, params);
281
224
  merged.buttons = mergeDeepObject(this._elementsDefault.buttons, merged.buttons);
@@ -285,68 +228,88 @@ class VGAlert {
285
228
  return merged;
286
229
  }
287
230
 
288
- /**
289
- * Создаёт и возвращает экземпляр модального окна с контентом алерта.
290
- *
291
- * @private
292
- * @returns {VGModal} Экземпляр модального окна.
293
- */
294
231
  _buildModal() {
295
- const id = `${CLASS_NAME_ALERT}-${crypto.randomUUID ? crypto.randomUUID().slice(0, 8) : makeRandomString()}`;
296
- const $modal = Selectors.find(`.${CLASS_NAME_ALERT}-modal`);
297
- if ($modal) $modal.remove();
232
+ const id = `${CLASS_NAME_ALERT}-${makeRandomString()}`;
298
233
 
299
- const html = Html('dom');
234
+ return VGModal.build(id, this._params.modal, (modal) => {
235
+ modal._element.classList.add(`${CLASS_NAME_ALERT}-modal`);
236
+ const body = Selectors.find(".vg-modal-body", modal._element);
237
+ body.append(this._buildContent());
238
+ });
239
+ }
300
240
 
301
- return VGModal.build(id, this._params.modal, (modalInstance) => {
302
- const element = modalInstance._element;
303
- element.classList.add(`${CLASS_NAME_ALERT}-modal`);
304
- element.setAttribute("role", "alertdialog");
305
- element.setAttribute("aria-modal", "true");
241
+ _buildContent() {
242
+ const html = Html('dom');
306
243
 
307
- const $body = Selectors.find(".vg-modal-body", element);
308
- if (!$body) return;
244
+ let icon = null;
309
245
 
310
- let icon = null;
311
- if (this._params.icon) {
312
- icon = html.div({class: `${CLASS_NAME_ALERT}-content--icon`}, this._params.icon, {isHTML: true});
313
- }
246
+ if (this._params.icon) {
247
+ icon = html.div(
248
+ {class: `${CLASS_NAME_ALERT}-content--icon`},
249
+ this._params.icon,
250
+ {isHTML: true}
251
+ );
252
+ }
314
253
 
315
- const buttons = document.createElement("div");
316
- Classes.add(buttons, "vg-alert-buttons");
254
+ const buttons = document.createElement("div");
255
+ Classes.add(buttons, "vg-alert-buttons");
317
256
 
318
- if (this._params.mode === "confirm") {
319
- this._createButton(buttons, "cancel");
320
- this._createButton(buttons, "agree");
321
- }
257
+ if (this._params.mode === "confirm") {
258
+ this._createButton(buttons, "cancel");
259
+ this._createButton(buttons, "agree");
260
+ }
322
261
 
323
- if (this._params.mode === "info") {
324
- this._createButton(buttons, "cancel");
325
- }
262
+ if (this._params.mode === "info") {
263
+ this._createButton(buttons, "cancel");
264
+ }
326
265
 
327
- let wrapper = html.div({class: `${CLASS_NAME_ALERT}-wrapper ${CLASS_NAME_ALERT}-${this._params.theme}`}, [
328
- html.div({class: `${CLASS_NAME_ALERT}-content`}, [
329
- icon,
330
- html.div({class: `${CLASS_NAME_ALERT}-content--message`}, [
331
- html.div({class: `${CLASS_NAME_ALERT}-content--title`}, this._params.message.title),
332
- html.div({class: `${CLASS_NAME_ALERT}-content--description`}, this._params.message.description),
333
- ])
334
- ]),
266
+ return html.div(
267
+ {class: `${CLASS_NAME_ALERT}-wrapper ${CLASS_NAME_ALERT}-${this._params.theme}`},
268
+ [
269
+ html.div(
270
+ {class: `${CLASS_NAME_ALERT}-content`},
271
+ [
272
+ icon,
273
+ html.div(
274
+ {class: `${CLASS_NAME_ALERT}-content--message`},
275
+ [
276
+ html.div(
277
+ {class: `${CLASS_NAME_ALERT}-content--title`},
278
+ this._params.message.title
279
+ ),
280
+ html.div(
281
+ {class: `${CLASS_NAME_ALERT}-content--description`},
282
+ this._params.message.description
283
+ )
284
+ ]
285
+ )
286
+ ]
287
+ ),
335
288
  buttons
336
- ]);
289
+ ]
290
+ );
291
+ }
337
292
 
338
- $body.appendChild(wrapper);
339
- });
293
+ _buildOverlay() {
294
+ let targetContainers = ['.vg-modal', '.vg-sidebar'];
295
+ const containerWrap = this._params.relatedTarget.closest(targetContainers.join(', '));
296
+
297
+ const modal = VGModal.getOrCreateInstance(containerWrap);
298
+ const container = Selectors.find('.vg-modal-content', modal._element) || containerWrap;
299
+
300
+ const overlay = document.createElement('div');
301
+
302
+ overlay.className = `${CLASS_NAME_ALERT}-overlay`;
303
+ overlay.append(this._buildContent());
304
+
305
+ container.append(overlay);
306
+
307
+ return {
308
+ element: overlay,
309
+ render: modal,
310
+ };
340
311
  }
341
312
 
342
- /**
343
- * Создаёт кнопку и добавляет её в контейнер.
344
- *
345
- * @private
346
- * @param {HTMLElement} container - Родительский элемент.
347
- * @param {'agree'|'cancel'} key - Ключ кнопки.
348
- * @returns {void}
349
- */
350
313
  _createButton(container, key) {
351
314
  const button = this._params.buttons[key];
352
315
  if (!button || button.element) {
@@ -385,51 +348,25 @@ const EVENT_KEY_ACCEPT = `${NAME_KEY}.accept`;
385
348
  const EVENT_KEY_REJECT = `${NAME_KEY}.reject`;
386
349
  const EVENT_KEY_FINALLY = `${NAME_KEY}.finally`;
387
350
 
388
- /**
389
- * Класс для работы с алертами по data-атрибутам.
390
- *
391
- * @class
392
- * @extends BaseModule
393
- * @example
394
- * <button data-vg-toggle="alert" data-ajax-route="/delete/1">Удалить</button>
395
- */
396
351
  class VGAlertConfirm extends BaseModule {
397
- /**
398
- * Создаёт экземпляр VGAlertConfirm.
399
- *
400
- * @param {HTMLElement} element - DOM-элемент.
401
- * @param {AlertParams} options - Параметры.
402
- */
403
352
  constructor(element, options = {}) {
404
353
  super(element);
405
354
  this._params = this._getParams(element, mergeDeepObject({}, options));
406
355
  }
407
356
 
408
- /**
409
- * Возвращает имя модуля.
410
- * @returns {string}
411
- */
412
357
  static get NAME() {
413
358
  return NAME;
414
359
  }
415
360
 
416
- /**
417
- * Возвращает ключевое имя с префиксом.
418
- * @returns {string}
419
- */
420
361
  static get NAME_KEY() {
421
362
  return NAME_KEY;
422
363
  }
423
364
 
424
- /**
425
- * Запускает логику алерта: вызывает модальное окно и обрабатывает результат.
426
- *
427
- * @param {typeof VGAlert} AlertClass - Класс алерта.
428
- * @returns {void}
429
- */
430
365
  run(AlertClass) {
431
366
  if (this._params.mode !== "confirm") return;
432
367
 
368
+ this._params.relatedTarget = this._element;
369
+
433
370
  AlertClass.call(this._params)
434
371
  .then((resolve) => {
435
372
  if (!resolve.accepted) return Promise.reject(resolve);
@@ -447,12 +384,6 @@ class VGAlertConfirm extends BaseModule {
447
384
  });
448
385
  }
449
386
 
450
- /**
451
- * Выполняет AJAX-запрос после подтверждения.
452
- *
453
- * @private
454
- * @returns {Promise<Object>} Ответ от сервера.
455
- */
456
387
  _ajax() {
457
388
  return new Promise((resolve) => {
458
389
  this._route((status, data) => {
@@ -482,4 +413,4 @@ EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, functi
482
413
  });
483
414
  });
484
415
 
485
- export default VGAlert;
416
+ export default VGAlert;
@@ -11,10 +11,12 @@
11
11
  - Полная **локализация** (настройка языка)
12
12
  - Гибкая **настройка кнопок** и сообщений
13
13
  - Поддержка **SVG-иконок** и тем стилей (`danger`, `warning`, `success`, `info`)
14
- - Работа как через **API**, так и через **`data`-атрибуты**
15
- - Интеграция с промисами (`Promise`)
16
- - Защита от **множественных одновременных вызовов**
17
- - Поддержка **клавиатурных сокращений** (Enter, Escape)
14
+ - Работа как через **API**, так и через **`data`-атрибуты**
15
+ - Интеграция с промисами (`Promise`)
16
+ - Защита от **множественных одновременных вызовов**
17
+ - Поддержка **клавиатурных сокращений** (Enter, Escape)
18
+ - Два режима рендера: обычный **`modal`** и вложенный **`overlay`**
19
+ - Возможность закрывать родительскую модалку/сайдбар через `render.dismiss`
18
20
 
19
21
  ---
20
22
 
@@ -27,15 +29,45 @@ import VGAlert from "./app/modules/vgalert/js/vgalert.js";
27
29
 
28
30
  ---
29
31
 
30
- ## 🧩 Режимы работы
32
+ ## 🧩 Режимы работы
31
33
 
32
34
  ### 1. `confirm` — Подтверждение действия
33
35
  Показывает два действия: **Подтвердить** и **Отмена**.
34
36
 
35
- ### 2. `info` — Информационное сообщение
36
- Показывает только кнопку **ОК** (или "Продолжить").
37
-
38
- ---
37
+ ### 2. `info` — Информационное сообщение
38
+ Показывает только кнопку **ОК** (или "Продолжить").
39
+
40
+ ---
41
+
42
+ ## 🖼 Рендер (`render`)
43
+
44
+ `VGAlert` поддерживает два варианта отображения:
45
+
46
+ | Параметр | Значение | Описание |
47
+ |--------|--------|----------|
48
+ | `render.type` | `modal` | Обычный alert в отдельной модалке |
49
+ | `render.type` | `overlay` | Alert-оверлей внутри текущей `.vg-modal` или `.vg-sidebar` |
50
+ | `render.dismiss` | `true` / `false` | Для `overlay`: после закрытия alert дополнительно закрывает родительский контейнер |
51
+
52
+ ### Пример: alert поверх открытой модалки
53
+
54
+ ```js
55
+ VGAlert.call({
56
+ render: {
57
+ type: "overlay",
58
+ dismiss: true
59
+ },
60
+ relatedTarget: button,
61
+ message: {
62
+ title: "Удалить запись",
63
+ description: "Действие будет выполнено в текущем окне."
64
+ }
65
+ });
66
+ ```
67
+
68
+ `relatedTarget` нужен для `overlay`, чтобы модуль нашёл ближайшую `.vg-modal` или `.vg-sidebar`, куда надо встроить alert.
69
+
70
+ ---
39
71
 
40
72
  ## 🎨 Темы (`theme`)
41
73
  | Значение | Иконка | Назначение |
@@ -76,9 +108,9 @@ VGAlert.call({
76
108
  });
77
109
  ```
78
110
 
79
- ### Пример 2: Информационное сообщение
80
- ```js
81
- VGAlert.call({
111
+ ### Пример 2: Информационное сообщение
112
+ ```js
113
+ VGAlert.call({
82
114
  mode: "info",
83
115
  theme: "success",
84
116
  message: {
@@ -91,10 +123,26 @@ VGAlert.call({
91
123
  class: ["btn-success"]
92
124
  }
93
125
  }
94
- });
95
- ```
96
-
97
- ---
126
+ });
127
+ ```
128
+
129
+ ### Пример 3: Обработка результата через `Promise`
130
+ ```js
131
+ VGAlert.call({
132
+ message: {
133
+ title: "Подтвердите действие",
134
+ description: "Продолжить?"
135
+ }
136
+ }).then((result) => {
137
+ console.log(result.accepted); // true
138
+ console.log(result.timestamp);
139
+ }).catch((error) => {
140
+ console.log(error.accepted); // false
141
+ console.log(error.timestamp);
142
+ });
143
+ ```
144
+
145
+ ---
98
146
 
99
147
  ## 🔌 Работа с AJAX
100
148
 
@@ -121,24 +169,30 @@ VGAlert.confirm(element, {
121
169
 
122
170
  ---
123
171
 
124
- ## 🔔 События
125
-
126
- Модуль генерирует события:
127
-
128
- | Событие | Описание |
129
- |--------|--------|
130
- | `vg.alert.accept` | Пользователь подтвердил действие |
172
+ ## 🔔 События
173
+
174
+ Модуль генерирует события на элементе-триггере, у которого есть `[data-vg-toggle="alert"]`:
175
+
176
+ | Событие | Описание |
177
+ |--------|--------|
178
+ | `vg.alert.accept` | Пользователь подтвердил действие |
131
179
  | `vg.alert.reject` | Пользователь отменил действие |
132
- | `vg.alert.finally` | Действие завершено (в любом случае) |
133
- | `vg.alert.loaded` | Получен ответ от сервера (если был AJAX) |
134
-
135
- ```js
136
- document.addEventListener('vg.alert.accept', function(e) {
137
- console.log('Подтверждено:', e.vgalert);
138
- });
139
- ```
140
-
141
- ---
180
+ | `vg.alert.finally` | Действие завершено (в любом случае) |
181
+ | `vg.alert.loaded` | Получен ответ от сервера (если был AJAX) |
182
+
183
+ ```js
184
+ const buttons = document.querySelectorAll('[data-vg-toggle="alert"]');
185
+
186
+ buttons.forEach((button) => {
187
+ button.addEventListener('vg.alert.accept', function (e) {
188
+ console.log('Подтверждено:', e.vgalert);
189
+ });
190
+ });
191
+ ```
192
+
193
+ Если нужен общий глобальный обработчик, событие можно слушать и на `document`, так как оно всплывает.
194
+
195
+ ---
142
196
 
143
197
  ## ⌨️ Клавиатурные комбинации
144
198
 
@@ -149,12 +203,16 @@ document.addEventListener('vg.alert.accept', function(e) {
149
203
 
150
204
  ## 🛠️ Конфигурация по умолчанию
151
205
  ```js
152
- {
153
- mode: "confirm",
154
- theme: "danger",
155
- modal: {
156
- centered: false,
157
- backdrop: true,
206
+ {
207
+ mode: "confirm",
208
+ theme: "danger",
209
+ render: {
210
+ type: "modal",
211
+ dismiss: false
212
+ },
213
+ modal: {
214
+ centered: false,
215
+ backdrop: true,
158
216
  keyboard: true,
159
217
  dismiss: true
160
218
  },
@@ -191,12 +249,13 @@ VGAlert.call(params, 'en');
191
249
 
192
250
  ## 🧩 Кастомизация кнопок
193
251
 
194
- Поддерживает:
195
- - `tag: 'button' | 'a'`
196
- - `type` (для `<button>`)
197
- - `class` — массив CSS-классов
198
- - `attr` — любые дополнительные атрибуты
199
- - `element` — готовый HTML-код кнопки
252
+ Поддерживает:
253
+ - `tag: 'button' | 'a'`
254
+ - `type` (для `<button>`)
255
+ - `class` — массив CSS-классов
256
+ - `attr` — любые дополнительные атрибуты
257
+ - `element` — готовый HTML-код кнопки
258
+ - `toggle` — служебный атрибут кнопки (`data-vg-alert-agree` / `data-vg-alert-cancel`), обычно задаётся модулем автоматически
200
259
 
201
260
  ### Пример: кнопка-ссылка
202
261
  ```js
@@ -239,4 +298,4 @@ MIT — свободно используйте и модифицируйте.
239
298
  ---
240
299
 
241
300
  > 🚀 Автор: VEGAS STUDIO (vegas-dev.com)
242
- > 📍 Поддерживается в проектах VEGAS
301
+ > 📍 Поддерживается в проектах VEGAS
@@ -18,6 +18,24 @@
18
18
  --vg-modal-margin: 65px !important;
19
19
  }
20
20
 
21
+ &-overlay {
22
+ width: 100%;
23
+ height: 100%;
24
+ position: absolute;
25
+ top: 0;
26
+ left: 0;
27
+ background: rgba(0, 0, 0, 0.75);
28
+
29
+ .vg-alert-wrapper {
30
+ background-color: white;
31
+ width: 90%;
32
+ position: absolute;
33
+ left: 50%;
34
+ transform: translateX(-50%);
35
+ top: 1rem;
36
+ }
37
+ }
38
+
21
39
  &-wrapper {
22
40
  @include mix-vars('alert', $alert-map);
23
41
  @include mix-alert-color-mode($class: vg-alert);
@@ -0,0 +1,5 @@
1
+ import VGDynamicTable from "./js/vgdynamictable";
2
+ import Editable from "./js/editable";
3
+
4
+ export {Editable, VGDynamicTable};
5
+ export default VGDynamicTable;