vgapp 1.3.6 → 1.3.8

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
 
@@ -24,6 +24,9 @@
24
24
  * callback: {
25
25
  * afterSuccess: (form, instance, event, data) => {
26
26
  * console.log('Форма успешно отправлена');
27
+ * },
28
+ * afterValidateError: (form, instance, event, errors) => {
29
+ * console.log(errors);
27
30
  * }
28
31
  * }
29
32
  * });
@@ -162,6 +165,7 @@ class VGFormSender extends BaseModule {
162
165
  },
163
166
  callback: {
164
167
  afterInit: noop,
168
+ afterValidateError: noop,
165
169
  afterSuccess: noop,
166
170
  afterError: noop,
167
171
  afterSend: noop,
@@ -479,6 +483,47 @@ class VGFormSender extends BaseModule {
479
483
  }
480
484
  }
481
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
+
482
527
  /**
483
528
  * Отображение алерта в зависимости от типа (modal/collapse)
484
529
  * @param {Object|string} data - Данные для отображения
@@ -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,22 @@ 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
+ okaux.Modal.getOpenInstances().forEach((instance) => {
686
+ instance.hide();
687
+ });
688
+ }
689
+ });
634
690
  }
635
691
 
636
692
  /**
@@ -891,12 +947,7 @@ EventHandler.on(document, EVENT_SUBMIT_DATA_API, function (event) {
891
947
 
892
948
  if (instance._params.validate) {
893
949
  if (!instance._element.checkValidity()) {
894
- event.preventDefault();
895
- event.stopPropagation();
896
-
897
- instance._element.classList.add(instance._params.classes.wasValidate);
898
-
899
- return false;
950
+ return instance._handleValidateError(event);
900
951
  }
901
952
  }
902
953
 
@@ -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