vgapp 1.3.6 → 1.3.7

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.
@@ -112,12 +112,22 @@ class VGAlert {
112
112
  if (isAlertOpen) return Promise.reject({ accepted: false, reason: lang_messages(context.lang, NAME_KEY).reason });
113
113
  isAlertOpen = true;
114
114
 
115
- const getContainer = () => {
116
- if (context._params.render.type === "overlay") {
117
- const overlay = context._buildOverlay();
118
- return {
119
- element: overlay.element,
120
- render: overlay.render,
115
+ const getContainer = () => {
116
+ if (context._params.render.type === "overlay") {
117
+ const overlay = context._buildOverlay();
118
+
119
+ if (!overlay.element || !overlay.render) {
120
+ const modal = context._buildModal();
121
+ return {
122
+ element: modal._element,
123
+ render: modal,
124
+ type: 'modal'
125
+ }
126
+ }
127
+
128
+ return {
129
+ element: overlay.element,
130
+ render: overlay.render,
121
131
  type: 'overlay'
122
132
  }
123
133
  } else if (context._params.render.type === "modal") {
@@ -239,11 +249,11 @@ class VGAlert {
239
249
 
240
250
  this._isDataApiBound = true;
241
251
 
242
- EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
243
- event.preventDefault();
244
- const target = event.target;
245
-
246
- if (!isVisible(target) || !isElement(target)) return;
252
+ EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
253
+ event.preventDefault();
254
+ const target = event.delegateTarget || this || event.target?.closest(SELECTOR_DATA_TOGGLE) || event.target;
255
+
256
+ if (!isVisible(target) || !isElement(target)) return;
247
257
 
248
258
  VGAlert.confirm(target, {
249
259
  buttons: {
@@ -329,12 +339,22 @@ class VGAlert {
329
339
  );
330
340
  }
331
341
 
332
- _buildOverlay() {
333
- let targetContainers = ['.vg-modal', '.vg-sidebar'];
334
- const containerWrap = this._params.relatedTarget.closest(targetContainers.join(', '));
335
-
336
- const modal = VGModal.getOrCreateInstance(containerWrap);
337
- const container = Selectors.find('.vg-modal-content', modal._element) || containerWrap;
342
+ _buildOverlay() {
343
+ const targetContainers = ['.vg-modal', '.vg-sidebar'];
344
+ const relatedTarget = this._params.relatedTarget;
345
+ const containerWrap = isElement(relatedTarget)
346
+ ? relatedTarget.closest(targetContainers.join(', '))
347
+ : null;
348
+
349
+ if (!containerWrap) {
350
+ return {
351
+ element: null,
352
+ render: null,
353
+ };
354
+ }
355
+
356
+ const modal = VGModal.getOrCreateInstance(containerWrap);
357
+ const container = Selectors.find('.vg-modal-content', modal._element) || containerWrap;
338
358
 
339
359
  const overlay = document.createElement('div');
340
360
 
@@ -21,13 +21,16 @@
21
21
  * type: 'modal',
22
22
  * enabled: true
23
23
  * },
24
- * callback: {
25
- * afterSuccess: (form, instance, event, data) => {
26
- * console.log('Форма успешно отправлена');
27
- * }
28
- * }
29
- * });
30
- */
24
+ * callback: {
25
+ * afterSuccess: (form, instance, event, data) => {
26
+ * console.log('Форма успешно отправлена');
27
+ * },
28
+ * afterValidateError: (form, instance, event, errors) => {
29
+ * console.log(errors);
30
+ * }
31
+ * }
32
+ * });
33
+ */
31
34
 
32
35
  import BaseModule from "../../base-module";
33
36
  import VGModal from "../../vgmodal/js/vgmodal";
@@ -160,12 +163,13 @@ class VGFormSender extends BaseModule {
160
163
  wasValidate: 'was-validated',
161
164
  content: 'vg-form-sender--content'
162
165
  },
163
- callback: {
164
- afterInit: noop,
165
- afterSuccess: noop,
166
- afterError: noop,
167
- afterSend: noop,
168
- },
166
+ callback: {
167
+ afterInit: noop,
168
+ afterValidateError: noop,
169
+ afterSuccess: noop,
170
+ afterError: noop,
171
+ afterSend: noop,
172
+ },
169
173
  interceptors: {
170
174
  beforeSend: () => new Promise((resolve, reject) => resolve()),
171
175
  success: false,
@@ -469,18 +473,59 @@ class VGFormSender extends BaseModule {
469
473
  * @param {'success'|'error'} status - Статус ответа
470
474
  * @private
471
475
  */
472
- _jsonParse(data, status) {
473
- const _this = this;
476
+ _jsonParse(data, status) {
477
+ const _this = this;
474
478
 
475
479
  if (_this._params.isJsonParse) {
476
480
  _this.alert(normalizeData(data), status);
477
481
  } else {
478
482
  _this.alert(data, status);
479
- }
480
- }
481
-
482
- /**
483
- * Отображение алерта в зависимости от типа (modal/collapse)
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Возвращает список ошибок нативной валидации формы
488
+ * @returns {Array<Object>}
489
+ * @private
490
+ */
491
+ _getValidationErrors() {
492
+ return [...this._element.elements]
493
+ .filter((field) => typeof field?.checkValidity === 'function' && !field.checkValidity())
494
+ .map((field) => ({
495
+ element: field,
496
+ name: field.name || '',
497
+ id: field.id || '',
498
+ type: field.type || '',
499
+ value: field.value,
500
+ message: field.validationMessage || '',
501
+ validity: {...field.validity}
502
+ }));
503
+ }
504
+
505
+ /**
506
+ * Обрабатывает неуспешную нативную валидацию перед отправкой
507
+ * @param {Event} event
508
+ * @returns {boolean}
509
+ * @private
510
+ */
511
+ _handleValidateError(event) {
512
+ event.preventDefault();
513
+ event.stopPropagation();
514
+
515
+ this._element.classList.add(this._params.classes.wasValidate);
516
+
517
+ execute(this._params.callback.afterValidateError, [
518
+ this._element,
519
+ this,
520
+ event,
521
+ this._getValidationErrors()
522
+ ]);
523
+
524
+ return false;
525
+ }
526
+
527
+ /**
528
+ * Отображение алерта в зависимости от типа (modal/collapse)
484
529
  * @param {Object|string} data - Данные для отображения
485
530
  * @param {string} status - Статус: success, error, danger и т.д.
486
531
  */
@@ -602,7 +647,7 @@ class VGFormSender extends BaseModule {
602
647
  * @private
603
648
  */
604
649
  _getNextModalZIndex() {
605
- const indexes = Selectors.findAll('.modal.show, .vg-modal.show')
650
+ const indexes = Selectors.findAll('.modal.show, .vg-modal.show, .ox-modal.is-open')
606
651
  .map((element) => Number.parseInt(window.getComputedStyle(element).zIndex, 10))
607
652
  .filter((index) => Number.isFinite(index));
608
653
 
@@ -614,6 +659,7 @@ class VGFormSender extends BaseModule {
614
659
  * @private
615
660
  */
616
661
  _closeOpenedModals() {
662
+ // Модалка Bootstrap
617
663
  [...document.getElementsByClassName('modal')].forEach((element) => {
618
664
  if (element && element.classList.contains('show')) {
619
665
  if (typeof bootstrap !== 'undefined' && bootstrap.Modal?.getOrCreateInstance) {
@@ -625,12 +671,21 @@ class VGFormSender extends BaseModule {
625
671
  }
626
672
  });
627
673
 
674
+ // Модалка VGApp
628
675
  [...document.getElementsByClassName('vg-modal')].forEach((element) => {
629
676
  if (element && element.classList.contains('show')) {
630
677
  const mVG = VGModal.getOrCreateInstance(element);
631
678
  mVG.hide([mVG]);
632
679
  }
633
680
  });
681
+
682
+ // Модалка OKAUX
683
+ [...document.getElementsByClassName('ox-modal')].forEach((element) => {
684
+ if (element && element.classList.contains('is-open')) {
685
+ const mOX = okaux.Modal.getOrCreateInstance(element);
686
+ mOX.hide();
687
+ }
688
+ });
634
689
  }
635
690
 
636
691
  /**
@@ -889,16 +944,11 @@ EventHandler.on(document, EVENT_SUBMIT_DATA_API, function (event) {
889
944
  return;
890
945
  }
891
946
 
892
- if (instance._params.validate) {
893
- if (!instance._element.checkValidity()) {
894
- event.preventDefault();
895
- event.stopPropagation();
896
-
897
- instance._element.classList.add(instance._params.classes.wasValidate);
898
-
899
- return false;
900
- }
901
- }
947
+ if (instance._params.validate) {
948
+ if (!instance._element.checkValidity()) {
949
+ return instance._handleValidateError(event);
950
+ }
951
+ }
902
952
 
903
953
  if (!instance._params.submit) {
904
954
  event.preventDefault();
@@ -43,10 +43,13 @@ VGFormSender.init(document.getElementById('contactForm'), {
43
43
  enabled: true,
44
44
  delay: 5000 // ожидание открытия, через 5 секунд
45
45
  },
46
- callback: {
47
- afterSuccess: (form, instance, event, data) => {
48
- console.log('Форма отправлена!', data);
49
- },
46
+ callback: {
47
+ afterValidateError: (form, instance, event, errors) => {
48
+ console.log('Ошибки валидации:', errors);
49
+ },
50
+ afterSuccess: (form, instance, event, data) => {
51
+ console.log('Форма отправлена!', data);
52
+ },
50
53
  afterError: (form, instance, event, data) => {
51
54
  console.error('Ошибка:', data);
52
55
  }
@@ -94,12 +97,40 @@ VGFormSender.init(document.getElementById('contactForm'), {
94
97
  | `interceptors.beforeSend` | `Function` | `Promise.resolve()` | Выполняется перед отправкой |
95
98
  | `interceptors.success` | `Function\|false` | `false` | Кастомная обработка успеха |
96
99
  | `interceptors.error` | `Function\|false` | `false` | Кастомная обработка ошибки |
97
- | `callback.afterInit` | `Function` | `noop` | После инициализации |
98
- | `callback.afterSuccess` | `Function` | `noop` | После успеха |
99
- | `callback.afterError` | `Function` | `noop` | После ошибки |
100
- | `callback.afterSend` | `Function` | `noop` | После любого ответа |
101
-
102
- ---
100
+ | `callback.afterInit` | `Function` | `noop` | После инициализации |
101
+ | `callback.afterValidateError` | `Function` | `noop` | После провала нативной HTML5-валидации, до отправки формы |
102
+ | `callback.afterSuccess` | `Function` | `noop` | После успеха |
103
+ | `callback.afterError` | `Function` | `noop` | После ошибки |
104
+ | `callback.afterSend` | `Function` | `noop` | После любого ответа |
105
+
106
+ ---
107
+
108
+ ## Проверка валидации до отправки
109
+
110
+ Если включён `validate: true`, модуль сначала вызывает нативный `checkValidity()` у формы. Если форма невалидна, отправка отменяется, форме добавляется класс `was-validated`, а затем вызывается `callback.afterValidateError`.
111
+
112
+ ```js
113
+ VGFormSender.init(document.getElementById('contactForm'), {
114
+ validate: true,
115
+ callback: {
116
+ afterValidateError: (form, instance, event, errors) => {
117
+ errors.forEach((error) => {
118
+ console.log(error.name, error.message);
119
+ });
120
+ }
121
+ }
122
+ });
123
+ ```
124
+
125
+ Каждый элемент массива `errors` содержит:
126
+
127
+ - `element` — ссылку на невалидное поле
128
+ - `name` / `id` / `type`
129
+ - `value` — текущее значение поля
130
+ - `message` — текст из `validationMessage`
131
+ - `validity` — слепок объекта `ValidityState`
132
+
133
+ ---
103
134
 
104
135
  ## 🔔 События
105
136