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.
- package/CHANGELOG.md +9 -0
- package/LICENSE +22 -0
- package/app/langs/en/buttons.json +10 -0
- package/app/langs/en/messages.json +32 -0
- package/app/langs/en/titles.json +6 -0
- package/app/langs/ru/buttons.json +10 -0
- package/app/langs/ru/messages.json +32 -0
- package/app/langs/ru/titles.json +6 -0
- package/app/modules/base-module.js +23 -2
- package/app/modules/module-fn.js +20 -9
- package/app/modules/vgalert/js/vgalert.js +362 -214
- package/app/modules/vgalert/readme.md +242 -0
- package/app/modules/vgcollapse/js/vgcollapse.js +216 -62
- package/app/modules/vgcollapse/readme.md +56 -0
- package/app/modules/vgcollapse/scss/_variables.scss +5 -0
- package/app/modules/vgcollapse/scss/vgcollapse.scss +41 -0
- package/app/modules/vgdropdown/js/vgdropdown.js +140 -38
- package/app/modules/vgdropdown/readme.md +225 -0
- package/app/modules/vgfiles/js/base.js +499 -0
- package/app/modules/vgfiles/js/droppable.js +159 -0
- package/app/modules/vgfiles/js/loader.js +389 -0
- package/app/modules/vgfiles/js/render.js +83 -0
- package/app/modules/vgfiles/js/sortable.js +155 -0
- package/app/modules/vgfiles/js/vgfiles.js +796 -280
- package/app/modules/vgfiles/readme.md +193 -0
- package/app/modules/vgfiles/scss/_animations.scss +18 -0
- package/app/modules/vgfiles/scss/_mixins.scss +73 -0
- package/app/modules/vgfiles/scss/_variables.scss +103 -26
- package/app/modules/vgfiles/scss/vgfiles.scss +573 -60
- package/app/modules/vgformsender/js/vgformsender.js +5 -1
- package/app/modules/vgformsender/readme.md +30 -1
- package/app/modules/vglawcookie/js/vglawcookie.js +96 -62
- package/app/modules/vglawcookie/readme.md +102 -0
- package/app/modules/vgsidebar/js/vgsidebar.js +6 -4
- package/app/utils/js/components/ajax.js +176 -104
- package/app/utils/js/components/animation.js +124 -39
- package/app/utils/js/components/backdrop.js +54 -31
- package/app/utils/js/components/lang.js +71 -64
- package/app/utils/js/components/params.js +34 -31
- package/app/utils/js/components/scrollbar.js +118 -67
- package/app/utils/js/components/templater.js +14 -4
- package/app/utils/js/dom/cookie.js +107 -64
- package/app/utils/js/dom/data.js +68 -20
- package/app/utils/js/dom/event.js +272 -239
- package/app/utils/js/dom/manipulator.js +135 -62
- package/app/utils/js/dom/selectors.js +134 -59
- package/app/utils/js/functions.js +183 -349
- package/build/vgapp.css +1 -1
- package/build/vgapp.css.map +1 -1
- package/index.scss +3 -0
- package/package.json +1 -1
- package/app/utils/js/components/overflow.js +0 -28
|
@@ -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
|
|
4
|
+
import { isElement, isVisible, makeRandomString, mergeDeepObject } from "../../../utils/js/functions";
|
|
5
|
+
import { getSVG } from "../../module-fn";
|
|
6
|
+
import { Classes} 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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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:
|
|
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:
|
|
67
|
-
out:
|
|
126
|
+
in: "animate__rollIn",
|
|
127
|
+
out: "animate__rollOut",
|
|
68
128
|
delay: 300,
|
|
69
|
-
duration: 700
|
|
129
|
+
duration: 700,
|
|
70
130
|
},
|
|
71
131
|
},
|
|
72
|
-
mode:
|
|
73
|
-
theme:
|
|
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
|
-
|
|
82
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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,284 @@ 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
|
|
105
|
-
if (agreeBtn)
|
|
106
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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 ===
|
|
124
|
-
if (cancelBtn) cancelBtn.addEventListener(
|
|
233
|
+
if (context._params.mode === "info") {
|
|
234
|
+
if (cancelBtn) cancelBtn.addEventListener("click", handleCancel);
|
|
235
|
+
}
|
|
125
236
|
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
130
|
-
|
|
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
|
|
140
|
-
|
|
259
|
+
let lang = 'ru';
|
|
260
|
+
|
|
261
|
+
if ('lang' in options) {
|
|
262
|
+
lang = options.lang || 'ru';
|
|
263
|
+
delete options.lang;
|
|
264
|
+
}
|
|
265
|
+
const context = new VGAlert(options, lang);
|
|
266
|
+
if (context._params.mode !== "confirm") return;
|
|
141
267
|
|
|
142
268
|
const instance = VGAlertConfirm.getOrCreateInstance(elem, context._params);
|
|
143
269
|
instance.run(VGAlert);
|
|
144
270
|
}
|
|
145
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Слияние пользовательских параметров с дефолтными.
|
|
274
|
+
*
|
|
275
|
+
* @private
|
|
276
|
+
* @param {AlertParams} params - Пользовательские параметры.
|
|
277
|
+
* @returns {AlertParams} Полный объект параметров.
|
|
278
|
+
*/
|
|
146
279
|
_setParams(params) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
280
|
+
const merged = mergeDeepObject(this._defaultParams, params);
|
|
281
|
+
merged.buttons = mergeDeepObject(this._elementsDefault.buttons, merged.buttons);
|
|
282
|
+
merged.message = mergeDeepObject(this._elementsDefault.message, merged.message);
|
|
283
|
+
merged.icon = this._elementsDefault.icons[merged.theme];
|
|
151
284
|
|
|
152
|
-
return
|
|
285
|
+
return merged;
|
|
153
286
|
}
|
|
154
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Создаёт и возвращает экземпляр модального окна с контентом алерта.
|
|
290
|
+
*
|
|
291
|
+
* @private
|
|
292
|
+
* @returns {VGModal} Экземпляр модального окна.
|
|
293
|
+
*/
|
|
155
294
|
_buildModal() {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
295
|
+
const id = `${CLASS_NAME_ALERT}-${crypto.randomUUID ? crypto.randomUUID().slice(0, 8) : makeRandomString()}`;
|
|
296
|
+
const $modal = Selectors.find(`.${CLASS_NAME_ALERT}-modal`);
|
|
159
297
|
if ($modal) $modal.remove();
|
|
160
298
|
|
|
161
|
-
|
|
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');
|
|
299
|
+
const html = Html('dom');
|
|
184
300
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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");
|
|
188
306
|
|
|
189
|
-
|
|
190
|
-
|
|
307
|
+
const $body = Selectors.find(".vg-modal-body", element);
|
|
308
|
+
if (!$body) return;
|
|
191
309
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
Classes.add(buttons, 'vg-alert-buttons');
|
|
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
|
+
}
|
|
197
314
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
this._create(buttons, 'button', 'agree');
|
|
201
|
-
}
|
|
315
|
+
const buttons = document.createElement("div");
|
|
316
|
+
Classes.add(buttons, "vg-alert-buttons");
|
|
202
317
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
318
|
+
if (this._params.mode === "confirm") {
|
|
319
|
+
this._createButton(buttons, "cancel");
|
|
320
|
+
this._createButton(buttons, "agree");
|
|
321
|
+
}
|
|
206
322
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
$body.append(wrapper);
|
|
323
|
+
if (this._params.mode === "info") {
|
|
324
|
+
this._createButton(buttons, "cancel");
|
|
210
325
|
}
|
|
326
|
+
|
|
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
|
+
]),
|
|
335
|
+
buttons
|
|
336
|
+
]);
|
|
337
|
+
|
|
338
|
+
$body.appendChild(wrapper);
|
|
211
339
|
});
|
|
212
340
|
}
|
|
213
341
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
Manipulator.set(btn, key, attr[key]);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
342
|
+
/**
|
|
343
|
+
* Создаёт кнопку и добавляет её в контейнер.
|
|
344
|
+
*
|
|
345
|
+
* @private
|
|
346
|
+
* @param {HTMLElement} container - Родительский элемент.
|
|
347
|
+
* @param {'agree'|'cancel'} key - Ключ кнопки.
|
|
348
|
+
* @returns {void}
|
|
349
|
+
*/
|
|
350
|
+
_createButton(container, key) {
|
|
351
|
+
const button = this._params.buttons[key];
|
|
352
|
+
if (!button || button.element) {
|
|
353
|
+
container.insertAdjacentHTML("beforeend", button?.element || "");
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
231
356
|
|
|
232
|
-
|
|
233
|
-
btn.innerHTML = button.text;
|
|
357
|
+
if (!button.tag) return;
|
|
234
358
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
359
|
+
let btn = null,
|
|
360
|
+
classes = [...new Set(button.class)].join(" "),
|
|
361
|
+
attrs = mergeDeepObject({
|
|
362
|
+
class: classes
|
|
363
|
+
}, button.attr);
|
|
238
364
|
|
|
239
|
-
if (
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
365
|
+
if (button.tag === "button") {
|
|
366
|
+
btn = Html('dom').button(button.text, button.type, attrs);
|
|
367
|
+
} else if (button.tag === "a") {
|
|
368
|
+
btn = Html('dom').a('#', button.text, attrs);
|
|
243
369
|
}
|
|
244
370
|
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
371
|
+
if (!btn) return;
|
|
372
|
+
|
|
373
|
+
btn.setAttribute(button.toggle, "true");
|
|
374
|
+
container.appendChild(btn);
|
|
250
375
|
}
|
|
251
376
|
}
|
|
252
377
|
|
|
253
|
-
|
|
254
378
|
/**
|
|
255
|
-
*
|
|
379
|
+
* Константы для событий и селекторов
|
|
256
380
|
*/
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
const
|
|
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';
|
|
381
|
+
const SELECTOR_DATA_TOGGLE = `[data-vg-toggle="${NAME}"]`;
|
|
382
|
+
const EVENT_KEY_CLICK_DATA_API = `click.${NAME_KEY}.data.api`;
|
|
383
|
+
const EVENT_KEY_LOADED = `${NAME_KEY}.loaded`;
|
|
384
|
+
const EVENT_KEY_ACCEPT = `${NAME_KEY}.accept`;
|
|
385
|
+
const EVENT_KEY_REJECT = `${NAME_KEY}.reject`;
|
|
386
|
+
const EVENT_KEY_FINALLY = `${NAME_KEY}.finally`;
|
|
266
387
|
|
|
388
|
+
/**
|
|
389
|
+
* Класс для работы с алертами по data-атрибутам.
|
|
390
|
+
*
|
|
391
|
+
* @class
|
|
392
|
+
* @extends BaseModule
|
|
393
|
+
* @example
|
|
394
|
+
* <button data-vg-toggle="alert" data-ajax-route="/delete/1">Удалить</button>
|
|
395
|
+
*/
|
|
267
396
|
class VGAlertConfirm extends BaseModule {
|
|
397
|
+
/**
|
|
398
|
+
* Создаёт экземпляр VGAlertConfirm.
|
|
399
|
+
*
|
|
400
|
+
* @param {HTMLElement} element - DOM-элемент.
|
|
401
|
+
* @param {AlertParams} options - Параметры.
|
|
402
|
+
*/
|
|
268
403
|
constructor(element, options = {}) {
|
|
269
404
|
super(element);
|
|
270
|
-
|
|
271
|
-
this._params = this._getParams(this._element, mergeDeepObject({
|
|
272
|
-
|
|
273
|
-
}, options));
|
|
405
|
+
this._params = this._getParams(element, mergeDeepObject({}, options));
|
|
274
406
|
}
|
|
275
407
|
|
|
408
|
+
/**
|
|
409
|
+
* Возвращает имя модуля.
|
|
410
|
+
* @returns {string}
|
|
411
|
+
*/
|
|
276
412
|
static get NAME() {
|
|
277
413
|
return NAME;
|
|
278
414
|
}
|
|
279
415
|
|
|
416
|
+
/**
|
|
417
|
+
* Возвращает ключевое имя с префиксом.
|
|
418
|
+
* @returns {string}
|
|
419
|
+
*/
|
|
280
420
|
static get NAME_KEY() {
|
|
281
|
-
return NAME_KEY
|
|
421
|
+
return NAME_KEY;
|
|
282
422
|
}
|
|
283
423
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
424
|
+
/**
|
|
425
|
+
* Запускает логику алерта: вызывает модальное окно и обрабатывает результат.
|
|
426
|
+
*
|
|
427
|
+
* @param {typeof VGAlert} AlertClass - Класс алерта.
|
|
428
|
+
* @returns {void}
|
|
429
|
+
*/
|
|
430
|
+
run(AlertClass) {
|
|
431
|
+
if (this._params.mode !== "confirm") return;
|
|
432
|
+
|
|
433
|
+
AlertClass.call(this._params)
|
|
434
|
+
.then((resolve) => {
|
|
435
|
+
if (!resolve.accepted) return Promise.reject(resolve);
|
|
436
|
+
if (!this._params.ajax.route) return resolve;
|
|
437
|
+
return this._ajax();
|
|
438
|
+
})
|
|
439
|
+
.then((response) => {
|
|
440
|
+
EventHandler.trigger(this._element, EVENT_KEY_ACCEPT, { vgalert: response });
|
|
441
|
+
})
|
|
442
|
+
.catch((error) => {
|
|
443
|
+
EventHandler.trigger(this._element, EVENT_KEY_REJECT, { vgalert: error });
|
|
444
|
+
})
|
|
445
|
+
.finally(() => {
|
|
446
|
+
EventHandler.trigger(this._element, EVENT_KEY_FINALLY, { vgalert: 'finally' });
|
|
447
|
+
});
|
|
300
448
|
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Выполняет AJAX-запрос после подтверждения.
|
|
452
|
+
*
|
|
453
|
+
* @private
|
|
454
|
+
* @returns {Promise<Object>} Ответ от сервера.
|
|
455
|
+
*/
|
|
301
456
|
_ajax() {
|
|
302
457
|
return new Promise((resolve) => {
|
|
303
458
|
this._route((status, data) => {
|
|
304
|
-
EventHandler.trigger(this._element, EVENT_KEY_LOADED, {stats: status, data
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
status: status,
|
|
308
|
-
data: data
|
|
309
|
-
});
|
|
310
|
-
})
|
|
459
|
+
EventHandler.trigger(this._element, EVENT_KEY_LOADED, { stats: status, data });
|
|
460
|
+
resolve({ status, data });
|
|
461
|
+
});
|
|
311
462
|
});
|
|
312
463
|
}
|
|
313
464
|
}
|
|
314
465
|
|
|
315
|
-
|
|
466
|
+
// Делегирование кликов по data-атрибутам
|
|
467
|
+
EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
|
316
468
|
event.preventDefault();
|
|
469
|
+
const target = event.target;
|
|
317
470
|
|
|
318
|
-
let target = event.target;
|
|
319
471
|
if (!isVisible(target) || !isElement(target)) return;
|
|
320
472
|
|
|
321
473
|
VGAlert.confirm(target, {
|
|
322
|
-
message: {
|
|
323
|
-
title: 'Удалить этот товар',
|
|
324
|
-
description: 'Внимание этот товар будет удален'
|
|
325
|
-
},
|
|
326
474
|
buttons: {
|
|
327
475
|
agree: {
|
|
328
|
-
class: [
|
|
476
|
+
class: ["btn-primary"],
|
|
329
477
|
},
|
|
330
478
|
cancel: {
|
|
331
|
-
class: [
|
|
332
|
-
}
|
|
333
|
-
}
|
|
479
|
+
class: ["btn-outline-primary"],
|
|
480
|
+
},
|
|
481
|
+
},
|
|
334
482
|
});
|
|
335
|
-
})
|
|
483
|
+
});
|
|
336
484
|
|
|
337
485
|
export default VGAlert;
|