vgapp 0.7.8 → 0.7.9

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.
@@ -1,56 +1,116 @@
1
1
  import BaseModule from "../../base-module";
2
2
  import VGModal from "../../vgmodal";
3
3
 
4
- import {isElement, isVisible, makeRandomString, mergeDeepObject} from "../../../utils/js/functions";
5
- import {getSVG} from "../../module-fn";
6
- import {Classes, Manipulator} from "../../../utils/js/dom/manipulator";
4
+ import { isElement, isVisible, makeRandomString, mergeDeepObject } from "../../../utils/js/functions";
5
+ import { getSVG } from "../../module-fn";
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
+ import {lang_buttons, lang_messages} from "../../../utils/js/components/lang";
10
+ import Html from "../../../utils/js/components/templater";
9
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
+ */
64
+
65
+ /**
66
+ * Константы
67
+ */
10
68
  const CLASS_NAME_ALERT = "vg-alert";
69
+ const DATA_AGREE = "data-vg-alert-agree";
70
+ const DATA_CANCEL = "data-vg-alert-cancel";
11
71
 
12
- class VGAlert {
13
- constructor(params = {}) {
14
- this._elementsDefault = {
15
- buttons: {
16
- agree: {
17
- element: '',
18
- tag: 'button',
19
- attr: {
20
- type: 'button',
21
- },
22
- toggle: 'data-vg-alert-agree',
23
- class: ['btn'],
24
- text: 'Да, согласен'
25
- },
26
- cancel: {
27
- element: '',
28
- tag: 'button',
29
- attr: {
30
- type: 'button',
31
- },
32
- toggle: 'data-vg-alert-cancel',
33
- class: ['btn'],
34
- text: 'Отмена'
35
- }
36
- },
37
- message: {
38
- title: 'Заголовок по умолчанию',
39
- description: 'Описание текущего действия',
40
- },
41
- icons: {
42
- danger: getSVG('danger'),
43
- warning: getSVG('warning'),
44
- success: getSVG('success'),
45
- info: getSVG('info'),
46
- }
47
- }
72
+ const NAME = "alert";
73
+ const NAME_KEY = "vg.alert";
74
+
75
+ // Глобальная блокировка: предотвращаем открытие нескольких алертов
76
+ let isAlertOpen = false;
48
77
 
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
+ class VGAlert {
101
+ /**
102
+ * Создаёт экземпляр VGAlert.
103
+ *
104
+ * @param {AlertParams} params - Пользовательские параметры.
105
+ * @param {'ru'|'en'} [lang='ru'] - Язык интерфейса.
106
+ */
107
+ constructor(params = {}, lang = 'ru') {
108
+ this.lang = lang;
49
109
  this._defaultParams = {
50
110
  ajax: {
51
- route: '',
52
- target: '',
53
- method: 'get',
111
+ route: "",
112
+ target: "",
113
+ method: "get",
54
114
  loader: false,
55
115
  once: false,
56
116
  output: true,
@@ -63,32 +123,77 @@ class VGAlert {
63
123
  dismiss: true,
64
124
  animation: {
65
125
  enable: false,
66
- in: 'animate__rollIn',
67
- out: 'animate__rollOut',
126
+ in: "animate__rollIn",
127
+ out: "animate__rollOut",
68
128
  delay: 300,
69
- duration: 700
129
+ duration: 700,
70
130
  },
71
131
  },
72
- mode: 'confirm',
73
- theme: 'danger',
132
+ mode: "confirm",
133
+ theme: "danger",
74
134
  buttons: {},
75
135
  message: {},
76
136
  };
77
-
137
+ this._elementsDefault = {
138
+ buttons: {
139
+ agree: {
140
+ element: "",
141
+ tag: "button",
142
+ type: "button",
143
+ attr: {},
144
+ toggle: DATA_AGREE,
145
+ class: ["btn"],
146
+ text: lang_buttons(this.lang, NAME).agree,
147
+ },
148
+ cancel: {
149
+ element: "",
150
+ tag: "button",
151
+ type: "button",
152
+ attr: {},
153
+ toggle: DATA_CANCEL,
154
+ class: ["btn"],
155
+ text: lang_buttons(this.lang, NAME).cancel,
156
+ },
157
+ },
158
+ message: {
159
+ title: lang_messages(this.lang, NAME).title,
160
+ description: lang_messages(this.lang, NAME).description
161
+ },
162
+ icons: {
163
+ danger: getSVG("danger"),
164
+ warning: getSVG("warning"),
165
+ success: getSVG("success"),
166
+ info: getSVG("info"),
167
+ },
168
+ };
78
169
  this._params = this._setParams(params);
79
170
  }
80
171
 
81
- static call(options = {}) {
82
- const context = new VGAlert(options);
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
+ static call(options = {}, lang = 'ru') {
182
+ const context = new VGAlert(options, lang);
183
+
184
+ if (isAlertOpen) return Promise.reject({ accepted: false, reason: lang_messages(context.lang, NAME_KEY).reason });
185
+ isAlertOpen = true;
186
+
83
187
  let modal = context._buildModal();
84
188
  modal.show();
85
189
 
86
- let container = modal._element,
87
- agreeBtn = Selectors.find('[data-vg-alert-agree]', container),
88
- cancelBtn = Selectors.find('[data-vg-alert-cancel]', container);
190
+ const container = modal._element;
191
+ const agreeBtn = Selectors.find(`[${DATA_AGREE}]`, container);
192
+ const cancelBtn = Selectors.find(`[${DATA_CANCEL}]`, container);
89
193
 
90
194
  return new Promise((resolve, reject) => {
91
- const handleAgree = () => {
195
+ const handleAgree = (e) => {
196
+ e.preventDefault();
92
197
  cleanup();
93
198
  resolve({
94
199
  accepted: true,
@@ -97,241 +202,278 @@ class VGAlert {
97
202
  modal.hide();
98
203
  };
99
204
 
100
- const handleCancel = () => {
205
+ const handleCancel = (e) => {
206
+ e.preventDefault();
101
207
  modal.hide();
102
208
  };
103
209
 
104
- const cleanup = () => {
105
- if (agreeBtn) agreeBtn.removeEventListener('click', handleAgree);
106
- if (cancelBtn) cancelBtn.removeEventListener('click', handleCancel);
210
+ const handleKeydown = (e) => {
211
+ if (e.key === "Enter" && agreeBtn) {
212
+ e.preventDefault();
213
+ handleAgree(e);
214
+ }
215
+ if (e.key === "Escape") {
216
+ e.preventDefault();
217
+ handleCancel(e);
218
+ }
107
219
  };
108
220
 
109
- if (context._params.mode === 'confirm') {
110
- if (agreeBtn) agreeBtn.addEventListener('click', handleAgree);
111
- if (cancelBtn) cancelBtn.addEventListener('click', handleCancel);
112
-
113
- container.addEventListener('vg.modal.hide', () => {
114
- cleanup();
221
+ const cleanup = () => {
222
+ isAlertOpen = false;
223
+ document.removeEventListener("keydown", handleKeydown);
224
+ if (agreeBtn) agreeBtn.removeEventListener("click", handleAgree);
225
+ if (cancelBtn) cancelBtn.removeEventListener("click", handleCancel);
226
+ };
115
227
 
116
- reject({
117
- accepted: false,
118
- timestamp: new Date(),
119
- });
120
- })
228
+ if (context._params.mode === "confirm") {
229
+ if (agreeBtn) agreeBtn.addEventListener("click", handleAgree);
230
+ if (cancelBtn) cancelBtn.addEventListener("click", handleCancel);
121
231
  }
122
232
 
123
- if (context._params.mode === 'info') {
124
- if (cancelBtn) cancelBtn.addEventListener('click', handleCancel);
233
+ if (context._params.mode === "info") {
234
+ if (cancelBtn) cancelBtn.addEventListener("click", handleCancel);
235
+ }
125
236
 
126
- container.addEventListener('vg.modal.hide', () => {
127
- cleanup();
237
+ document.addEventListener("keydown", handleKeydown);
238
+ container.addEventListener("vg.modal.hide", () => {
239
+ cleanup();
240
+ reject({
241
+ accepted: false,
242
+ timestamp: new Date(),
243
+ });
244
+ });
128
245
 
129
- reject({
130
- accepted: false,
131
- timestamp: new Date(),
132
- });
133
- })
134
- }
135
- })
246
+ container.focus();
247
+ });
136
248
  }
137
249
 
250
+ /**
251
+ * Инициирует алерт подтверждения на основе DOM-элемента.
252
+ *
253
+ * @static
254
+ * @param {HTMLElement} elem - Элемент, вызвавший алерт.
255
+ * @param {AlertParams} options - Параметры алерта.
256
+ * @returns {void}
257
+ */
138
258
  static confirm(elem, options = {}) {
139
- let context = new VGAlert(options);
140
- if (context._params.mode !== 'confirm') return;
259
+ const context = new VGAlert(options);
260
+ if (context._params.mode !== "confirm") return;
141
261
 
142
262
  const instance = VGAlertConfirm.getOrCreateInstance(elem, context._params);
143
263
  instance.run(VGAlert);
144
264
  }
145
265
 
266
+ /**
267
+ * Слияние пользовательских параметров с дефолтными.
268
+ *
269
+ * @private
270
+ * @param {AlertParams} params - Пользовательские параметры.
271
+ * @returns {AlertParams} Полный объект параметров.
272
+ */
146
273
  _setParams(params) {
147
- params = mergeDeepObject(this._defaultParams, params);
148
- params.buttons = mergeDeepObject(this._elementsDefault.buttons, params.buttons);
149
- params.message = mergeDeepObject(this._elementsDefault.message, params.message);
150
- params.icon = this._elementsDefault.icons[params.theme];
274
+ const merged = mergeDeepObject(this._defaultParams, params);
275
+ merged.buttons = mergeDeepObject(this._elementsDefault.buttons, merged.buttons);
276
+ merged.message = mergeDeepObject(this._elementsDefault.message, merged.message);
277
+ merged.icon = this._elementsDefault.icons[merged.theme];
151
278
 
152
- return params;
279
+ return merged;
153
280
  }
154
281
 
282
+ /**
283
+ * Создаёт и возвращает экземпляр модального окна с контентом алерта.
284
+ *
285
+ * @private
286
+ * @returns {VGModal} Экземпляр модального окна.
287
+ */
155
288
  _buildModal() {
156
- let id = CLASS_NAME_ALERT + '-' + makeRandomString(),
157
- $modal = Selectors.find('.'+ CLASS_NAME_ALERT +'-modal');
158
-
289
+ const id = `${CLASS_NAME_ALERT}-${crypto.randomUUID ? crypto.randomUUID().slice(0, 8) : makeRandomString()}`;
290
+ const $modal = Selectors.find(`.${CLASS_NAME_ALERT}-modal`);
159
291
  if ($modal) $modal.remove();
160
292
 
161
- return VGModal.build(id, this._params.modal, (self) => {
162
- let element = self._element;
163
- element.classList.add(CLASS_NAME_ALERT + '-modal');
164
-
165
- let $body = Selectors.find('.vg-modal-body', element);
166
- if ($body) {
167
- let wrapper = document.createElement('div');
168
- Classes.add(wrapper, CLASS_NAME_ALERT + '-wrapper');
169
- Classes.add(wrapper, CLASS_NAME_ALERT + '-' + this._params.theme);
170
-
171
- let content = document.createElement('div');
172
- Classes.add(content, CLASS_NAME_ALERT + '-content');
173
-
174
- let icon = document.createElement('div');
175
- Classes.add(icon, CLASS_NAME_ALERT + '-content--icon');
176
- this._create(icon, 'icons', this._params.theme);
177
-
178
- let message = document.createElement('div');
179
- Classes.add(message, CLASS_NAME_ALERT + '-content--message');
180
-
181
- let title = document.createElement('div');
182
- Classes.add(title, CLASS_NAME_ALERT + '-content--title');
183
- this._create(title, 'messages', 'title');
293
+ const html = Html('dom');
184
294
 
185
- let description = document.createElement('div');
186
- Classes.add(description, CLASS_NAME_ALERT + '-content--description');
187
- this._create(description, 'messages', 'description');
295
+ return VGModal.build(id, this._params.modal, (modalInstance) => {
296
+ const element = modalInstance._element;
297
+ element.classList.add(`${CLASS_NAME_ALERT}-modal`);
298
+ element.setAttribute("role", "alertdialog");
299
+ element.setAttribute("aria-modal", "true");
188
300
 
189
- message.append(title);
190
- message.append(description);
301
+ const $body = Selectors.find(".vg-modal-body", element);
302
+ if (!$body) return;
191
303
 
192
- if (this._params.icon) content.append(icon);
193
- content.append(message);
194
-
195
- let buttons = document.createElement('div');
196
- Classes.add(buttons, 'vg-alert-buttons');
304
+ let icon = null;
305
+ if (this._params.icon) {
306
+ icon = html.div({class: `${CLASS_NAME_ALERT}-content--icon`}, this._params.icon, {isHTML: true});
307
+ }
197
308
 
198
- if (this._params.mode === 'confirm') {
199
- this._create(buttons, 'button', 'cancel');
200
- this._create(buttons, 'button', 'agree');
201
- }
309
+ const buttons = document.createElement("div");
310
+ Classes.add(buttons, "vg-alert-buttons");
202
311
 
203
- if (this._params.mode === 'info') {
204
- this._create(buttons, 'button', 'cancel');
205
- }
312
+ if (this._params.mode === "confirm") {
313
+ this._createButton(buttons, "cancel");
314
+ this._createButton(buttons, "agree");
315
+ }
206
316
 
207
- wrapper.append(content);
208
- wrapper.append(buttons);
209
- $body.append(wrapper);
317
+ if (this._params.mode === "info") {
318
+ this._createButton(buttons, "cancel");
210
319
  }
320
+
321
+ let wrapper = html.div({class: `${CLASS_NAME_ALERT}-wrapper ${CLASS_NAME_ALERT}-${this._params.theme}`}, [
322
+ html.div({class: `${CLASS_NAME_ALERT}-content`}, [
323
+ icon,
324
+ html.div({class: `${CLASS_NAME_ALERT}-content--message`}, [
325
+ html.div({class: `${CLASS_NAME_ALERT}-content--title`}, this._params.message.title),
326
+ html.div({class: `${CLASS_NAME_ALERT}-content--description`}, this._params.message.description),
327
+ ])
328
+ ]),
329
+ buttons
330
+ ]);
331
+
332
+ $body.appendChild(wrapper);
211
333
  });
212
334
  }
213
335
 
214
- _create(container, element, mode) {
215
- if (element === 'button') {
216
- let button = this._params.buttons[mode];
217
- if (button.element) {
218
- return container.innerHTML += button.element;
219
- } else {
220
- if (!button.tag) return;
221
-
222
- let btn = document.createElement(button.tag);
223
- Classes.add(btn, button.class.join(' '));
224
-
225
- if (button.attr) {
226
- let attr = button.attr;
227
- for (const key in attr) {
228
- Manipulator.set(btn, key, attr[key]);
229
- }
230
- }
336
+ /**
337
+ * Создаёт кнопку и добавляет её в контейнер.
338
+ *
339
+ * @private
340
+ * @param {HTMLElement} container - Родительский элемент.
341
+ * @param {'agree'|'cancel'} key - Ключ кнопки.
342
+ * @returns {void}
343
+ */
344
+ _createButton(container, key) {
345
+ const button = this._params.buttons[key];
346
+ if (!button || button.element) {
347
+ container.insertAdjacentHTML("beforeend", button?.element || "");
348
+ return;
349
+ }
231
350
 
232
- Manipulator.set(btn, button.toggle, true);
233
- btn.innerHTML = button.text;
351
+ if (!button.tag) return;
234
352
 
235
- container.append(btn);
236
- }
237
- }
353
+ let btn = null,
354
+ classes = [...new Set(button.class)].join(" "),
355
+ attrs = mergeDeepObject({
356
+ class: classes
357
+ }, button.attr);
238
358
 
239
- if (element === 'icons') {
240
- if (this._params.icon) {
241
- container.innerHTML = this._params.icon;
242
- }
359
+ if (button.tag === "button") {
360
+ btn = Html('dom').button(button.text, button.type, attrs);
361
+ } else if (button.tag === "a") {
362
+ btn = Html('dom').a('#', button.text, attrs);
243
363
  }
244
364
 
245
- if (element === 'messages') {
246
- if (this._params.message) {
247
- container.innerHTML = this._params.message[mode];
248
- }
249
- }
365
+ if (!btn) return;
366
+
367
+ btn.setAttribute(button.toggle, "true");
368
+ container.appendChild(btn);
250
369
  }
251
370
  }
252
371
 
253
-
254
372
  /**
255
- * Constants
373
+ * Константы для событий и селекторов
256
374
  */
257
- const NAME = 'alert';
258
- const NAME_KEY = 'vg.alert';
259
-
260
- const SELECTOR_DATA_TOGGLE = '[data-vg-toggle="alert"]';
261
- const EVENT_KEY_CLICK_DATA_API = `click.${NAME_KEY}.data.api`;
262
-
263
- const EVENT_KEY_LOADED = 'vg.alert.loaded';
264
- const EVENT_KEY_ACCEPT = 'vg.alert.accept';
265
- const EVENT_KEY_REJECT = 'vg.alert.reject';
375
+ const SELECTOR_DATA_TOGGLE = `[data-vg-toggle="${NAME}"]`;
376
+ const EVENT_KEY_CLICK_DATA_API = `click.${NAME_KEY}.data.api`;
377
+ const EVENT_KEY_LOADED = `${NAME_KEY}.loaded`;
378
+ const EVENT_KEY_ACCEPT = `${NAME_KEY}.accept`;
379
+ const EVENT_KEY_REJECT = `${NAME_KEY}.reject`;
380
+ const EVENT_KEY_FINALLY = `${NAME_KEY}.finally`;
266
381
 
382
+ /**
383
+ * Класс для работы с алертами по data-атрибутам.
384
+ *
385
+ * @class
386
+ * @extends BaseModule
387
+ * @example
388
+ * <button data-vg-toggle="alert" data-ajax-route="/delete/1">Удалить</button>
389
+ */
267
390
  class VGAlertConfirm extends BaseModule {
391
+ /**
392
+ * Создаёт экземпляр VGAlertConfirm.
393
+ *
394
+ * @param {HTMLElement} element - DOM-элемент.
395
+ * @param {AlertParams} options - Параметры.
396
+ */
268
397
  constructor(element, options = {}) {
269
398
  super(element);
270
-
271
- this._params = this._getParams(this._element, mergeDeepObject({
272
-
273
- }, options));
399
+ this._params = this._getParams(element, mergeDeepObject({}, options));
274
400
  }
275
401
 
402
+ /**
403
+ * Возвращает имя модуля.
404
+ * @returns {string}
405
+ */
276
406
  static get NAME() {
277
407
  return NAME;
278
408
  }
279
409
 
410
+ /**
411
+ * Возвращает ключевое имя с префиксом.
412
+ * @returns {string}
413
+ */
280
414
  static get NAME_KEY() {
281
- return NAME_KEY
415
+ return NAME_KEY;
282
416
  }
283
417
 
284
- run(self) {
285
- if (this._params.mode !== 'confirm') return;
286
-
287
- self.call(this._params).then((resolve) => {
288
- if (resolve.accepted) {
289
- if (this._params.ajax.route) {
290
- return this._ajax()
291
- } else {
292
- return resolve;
293
- }
294
- }
295
- }).then((response) => {
296
- EventHandler.trigger(this._element, EVENT_KEY_ACCEPT, {vgalert: response});
297
- }).catch((error) => {
298
- EventHandler.trigger(this._element, EVENT_KEY_REJECT, {vgalert: error});
299
- })
418
+ /**
419
+ * Запускает логику алерта: вызывает модальное окно и обрабатывает результат.
420
+ *
421
+ * @param {typeof VGAlert} AlertClass - Класс алерта.
422
+ * @returns {void}
423
+ */
424
+ run(AlertClass) {
425
+ if (this._params.mode !== "confirm") return;
426
+
427
+ AlertClass.call(this._params)
428
+ .then((resolve) => {
429
+ if (!resolve.accepted) return Promise.reject(resolve);
430
+ if (!this._params.ajax.route) return resolve;
431
+ return this._ajax();
432
+ })
433
+ .then((response) => {
434
+ EventHandler.trigger(this._element, EVENT_KEY_ACCEPT, { vgalert: response });
435
+ })
436
+ .catch((error) => {
437
+ EventHandler.trigger(this._element, EVENT_KEY_REJECT, { vgalert: error });
438
+ })
439
+ .finally(() => {
440
+ EventHandler.trigger(this._element, EVENT_KEY_FINALLY, { vgalert: 'finally' });
441
+ });
300
442
  }
443
+
444
+ /**
445
+ * Выполняет AJAX-запрос после подтверждения.
446
+ *
447
+ * @private
448
+ * @returns {Promise<Object>} Ответ от сервера.
449
+ */
301
450
  _ajax() {
302
451
  return new Promise((resolve) => {
303
452
  this._route((status, data) => {
304
- EventHandler.trigger(this._element, EVENT_KEY_LOADED, {stats: status, data: data});
305
-
306
- resolve({
307
- status: status,
308
- data: data
309
- });
310
- })
453
+ EventHandler.trigger(this._element, EVENT_KEY_LOADED, { stats: status, data });
454
+ resolve({ status, data });
455
+ });
311
456
  });
312
457
  }
313
458
  }
314
459
 
315
- EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, (event) => {
460
+ // Делегирование кликов по data-атрибутам
461
+ EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
316
462
  event.preventDefault();
463
+ const target = event.target;
317
464
 
318
- let target = event.target;
319
465
  if (!isVisible(target) || !isElement(target)) return;
320
466
 
321
467
  VGAlert.confirm(target, {
322
- message: {
323
- title: 'Удалить этот товар',
324
- description: 'Внимание этот товар будет удален'
325
- },
326
468
  buttons: {
327
469
  agree: {
328
- class: ['btn', 'btn-primary'],
470
+ class: ["btn-primary"],
329
471
  },
330
472
  cancel: {
331
- class: ['btn', 'btn-outline-primary'],
332
- }
333
- }
473
+ class: ["btn-outline-primary"],
474
+ },
475
+ },
334
476
  });
335
- })
477
+ });
336
478
 
337
479
  export default VGAlert;