vgapp 1.2.0 → 1.2.2

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.
@@ -51,11 +51,12 @@ class VGFiles extends VGFilesBase {
51
51
  maxConcurrent: 1,
52
52
  retryAttempts: 1,
53
53
  retryDelay: 1000,
54
- },
55
- removes: {
56
- all: { route: '', alert: true, toast: true, confirm: null },
57
- single: { route: '', alert: true, toast: true, confirm: null }
58
- },
54
+ },
55
+ removes: {
56
+ buttons: null,
57
+ all: { route: '', alert: true, toast: true, confirm: null, buttons: null },
58
+ single: { route: '', alert: true, toast: true, confirm: null, buttons: null }
59
+ },
59
60
  sortable: {
60
61
  enabled: false,
61
62
  route: '',
@@ -585,11 +586,11 @@ class VGFiles extends VGFilesBase {
585
586
  return { accepted: Boolean(result), data: null };
586
587
  }
587
588
 
588
- _runDefaultRemoveConfirm(trigger, params) {
589
- return new Promise((resolve) => {
590
- VGAlert.confirm(trigger, {
591
- lang: params.lang,
592
- ajax: params.ajax,
589
+ _runDefaultRemoveConfirm(trigger, params) {
590
+ return new Promise((resolve) => {
591
+ VGAlert.confirm(trigger, {
592
+ lang: params.lang,
593
+ ajax: params.ajax,
593
594
  buttons: params.buttons,
594
595
  message: params.message
595
596
  });
@@ -600,25 +601,58 @@ class VGFiles extends VGFilesBase {
600
601
 
601
602
  EventHandler.one(trigger, 'vg.alert.reject', () => {
602
603
  resolve({ accepted: false, data: null });
603
- });
604
- });
605
- }
606
-
607
- _confirmRemove(type, trigger, ajax, message) {
608
- const buttons = {
609
- agree: {
610
- text: lang_buttons(this._params.lang, NAME)['agree'],
611
- class: ["btn-danger"],
612
- },
613
- cancel: {
614
- text: lang_buttons(this._params.lang, NAME)['cancel'],
615
- class: ["btn-outline-danger"],
616
- },
617
- };
618
-
619
- const confirmParams = {
620
- type,
621
- trigger,
604
+ });
605
+ });
606
+ }
607
+
608
+ _isPlainObject(value) {
609
+ return value && typeof value === 'object' && !Array.isArray(value);
610
+ }
611
+
612
+ _mergeButtonConfigs(...configs) {
613
+ return configs.reduce((acc, config) => {
614
+ if (!this._isPlainObject(config)) return acc;
615
+
616
+ Object.entries(config).forEach(([key, value]) => {
617
+ if (this._isPlainObject(acc[key]) && this._isPlainObject(value)) {
618
+ acc[key] = this._mergeButtonConfigs(acc[key], value);
619
+ return;
620
+ }
621
+
622
+ acc[key] = Array.isArray(value) ? value.slice() : value;
623
+ });
624
+
625
+ return acc;
626
+ }, {});
627
+ }
628
+
629
+ _getDefaultRemoveConfirmButtons() {
630
+ return {
631
+ agree: {
632
+ text: lang_buttons(this._params.lang, NAME)['agree'],
633
+ class: ["btn-danger"],
634
+ },
635
+ cancel: {
636
+ text: lang_buttons(this._params.lang, NAME)['cancel'],
637
+ class: ["btn-outline-danger"],
638
+ },
639
+ };
640
+ }
641
+
642
+ _getRemoveConfirmButtons(type) {
643
+ return this._mergeButtonConfigs(
644
+ this._getDefaultRemoveConfirmButtons(),
645
+ this._params?.removes?.buttons,
646
+ this._params?.removes?.[type]?.buttons
647
+ );
648
+ }
649
+
650
+ _confirmRemove(type, trigger, ajax, message) {
651
+ const buttons = this._getRemoveConfirmButtons(type);
652
+
653
+ const confirmParams = {
654
+ type,
655
+ trigger,
622
656
  lang: this._params.lang,
623
657
  ajax,
624
658
  buttons,
@@ -17,10 +17,12 @@ const CLASS_NAME_SHOW = 'show';
17
17
  const EVENT_KEY_CLICK_DATA_API = `click.${NAME_KEY}.data.api`;
18
18
 
19
19
  class VGHideShowPass extends BaseModule{
20
- constructor(el, params = {}) {
21
- super(el, params);
22
-
23
- this._params = this._getParams(el, mergeDeepObject({}, params));
20
+ constructor(el, params = {}) {
21
+ super(el, params);
22
+
23
+ this._params = this._getParams(el, mergeDeepObject({
24
+ parentClass: 'vg-form-sender--content-pass'
25
+ }, params));
24
26
  }
25
27
 
26
28
  static get NAME() {
@@ -60,12 +62,15 @@ class VGHideShowPass extends BaseModule{
60
62
  instance.build(false);
61
63
  }
62
64
 
63
- build(isShow = false) {
64
- let classes = this._params.classes.join(' '), elm = '';
65
- const HTML = Html('string');
66
-
67
- if (!isShow) {
68
- elm = HTML.component('eye', {class: classes});
65
+ build(isShow = false) {
66
+ let classes = this._params.classes.join(' '), elm = '';
67
+ const HTML = Html('string');
68
+ if (this._element.parentElement && this._params.parentClass) {
69
+ this._element.parentElement.classList.add(this._params.parentClass);
70
+ }
71
+
72
+ if (!isShow) {
73
+ elm = HTML.component('eye', {class: classes});
69
74
  } else {
70
75
  elm = HTML.component('eye', {class: classes, type: 'hide'});
71
76
  }
@@ -95,4 +100,4 @@ EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, functi
95
100
  instance.toggle(this);
96
101
  });
97
102
 
98
- export default VGHideShowPass
103
+ export default VGHideShowPass
@@ -1,4 +1,4 @@
1
- /**
1
+ /**
2
2
  * VGFormSender Module
3
3
  *
4
4
  * Этот модуль отвечает за отправку форм с поддержкой AJAX, валидации, отображения уведомлений
@@ -66,7 +66,8 @@ const NAME_KEY = 'vg.fs';
66
66
  * CSS-класс для алертов
67
67
  * @type {string}
68
68
  */
69
- const CLASS_NAME_ALERT = 'vg-form-sender-alert';
69
+ const CLASS_NAME_ALERT = 'vg-form-sender-alert';
70
+ const CLASS_NAME_MODAL_STACKED = 'vg-modal-stacked';
70
71
 
71
72
  /**
72
73
  * Событие: успешная отправка формы
@@ -130,13 +131,19 @@ class VGFormSender extends BaseModule {
130
131
  classes: ['vg-form-sender--hide-show-pass'],
131
132
  insert: 'afterend'
132
133
  },
133
- alert: {
134
- enabled: true,
135
- type: 'modal',
136
- errors: true,
137
- delay: 0,
138
- toast: {}
139
- },
134
+ alert: {
135
+ enabled: true,
136
+ type: 'modal',
137
+ errors: true,
138
+ title: true,
139
+ delay: 0,
140
+ modal: {
141
+ closeModalsBeforeModal: true
142
+ },
143
+ toast: {
144
+ closeModalsBeforeToast: false
145
+ }
146
+ },
140
147
  ajax: {
141
148
  route: '',
142
149
  target: '',
@@ -524,59 +531,107 @@ class VGFormSender extends BaseModule {
524
531
  * @param {string} status - Статус (success/error)
525
532
  * @private
526
533
  */
527
- _alertModal(data, status) {
528
- const _this = this;
529
-
530
- // Закрытие всех открытых модальных окон
531
- [...document.getElementsByClassName('modal')].forEach(function (element) {
532
- if (element && element.classList.contains('show')) {
533
- if (typeof bootstrap !== 'undefined') {
534
- let mBS = bootstrap.Modal?.getOrCreateInstance(element);
535
- mBS.hide();
536
- } else {
537
- console.warn(lang_messages(_this._params.lang, NAME).bootstrap_not_found)
538
- }
539
- }
540
- });
541
-
542
- [...document.getElementsByClassName('vg-modal')].forEach(function (element) {
543
- if (element && element.classList.contains('show')) {
544
- const mVG = VGModal.getOrCreateInstance(element);
545
- mVG.hide([mVG]);
546
- }
547
- });
548
-
549
- let id = _this._params.classes.general + '-' + makeRandomString(),
550
- $modal = Selectors.find('.' + _this._params.classes.alertModal);
551
-
552
- if ($modal) $modal.remove();
553
-
554
- setTimeout(() => {
555
- VGModal.init(id, {
556
- dismiss: true,
557
- classes: {
558
- alert: _this._params.classes.alertModal
559
- }
560
- }, function (self) {
561
- let element = self._element;
562
- element.classList.add(_this._params.classes.alertModal);
563
-
564
- let $content = Selectors.find('.vg-modal-content', element);
565
- if ($content) $content.classList.add(CLASS_NAME_ALERT, CLASS_NAME_ALERT + '-' + status);
566
-
567
- let $body = Selectors.find('.vg-modal-body', element);
568
- if ($body) $body.append(_this.setDataRelationStatus(element, status, data, 'modal'));
569
-
570
- self.toggle();
571
-
572
- if (_this._params.alert.delay > 0) {
573
- setTimeout(() => {
574
- self.hide();
575
- }, _this._params.alert.delay)
576
- }
577
- });
578
- }, _this._params.timeout);
579
- }
534
+ _alertModal(data, status) {
535
+ const _this = this;
536
+ const closeModalsBeforeModal = _this._params.alert.modal?.closeModalsBeforeModal;
537
+ const isStackedModal = !closeModalsBeforeModal && _this._hasOpenedModals();
538
+
539
+ if (closeModalsBeforeModal) {
540
+ _this._closeOpenedModals();
541
+ }
542
+
543
+ let id = _this._params.classes.general + '-' + makeRandomString(),
544
+ $modal = Selectors.find('.' + _this._params.classes.alertModal);
545
+
546
+ if ($modal) $modal.remove();
547
+
548
+ setTimeout(() => {
549
+ VGModal.init(id, {
550
+ dismiss: true,
551
+ classes: {
552
+ alert: _this._params.classes.alertModal
553
+ }
554
+ }, function (self) {
555
+ let element = self._element;
556
+ element.classList.add(_this._params.classes.alertModal);
557
+ if (isStackedModal) {
558
+ _this._setStackedModalState(element);
559
+ }
560
+
561
+ let $content = Selectors.find('.vg-modal-content', element);
562
+ if ($content) $content.classList.add(CLASS_NAME_ALERT, CLASS_NAME_ALERT + '-' + status);
563
+
564
+ let $body = Selectors.find('.vg-modal-body', element);
565
+ if ($body) $body.append(_this.setDataRelationStatus(element, status, data, 'modal'));
566
+
567
+ self.toggle();
568
+
569
+ if (_this._params.alert.delay > 0) {
570
+ setTimeout(() => {
571
+ self.hide();
572
+ }, _this._params.alert.delay)
573
+ }
574
+ });
575
+ }, _this._params.timeout);
576
+ }
577
+
578
+ /**
579
+ * Проверка наличия открытых модальных окон
580
+ * @returns {boolean}
581
+ * @private
582
+ */
583
+ _hasOpenedModals() {
584
+ return Boolean(Selectors.find('.modal.show, .vg-modal.show'));
585
+ }
586
+
587
+ /**
588
+ * Помечает alert-modal как вторую модалку и поднимает ее над уже открытыми
589
+ * @param {HTMLElement} element
590
+ * @private
591
+ */
592
+ _setStackedModalState(element) {
593
+ if (!element) return;
594
+
595
+ element.classList.add(CLASS_NAME_MODAL_STACKED);
596
+ element.style.zIndex = this._getNextModalZIndex();
597
+ }
598
+
599
+ /**
600
+ * Вычисление z-index выше всех открытых модалок
601
+ * @returns {string}
602
+ * @private
603
+ */
604
+ _getNextModalZIndex() {
605
+ const indexes = Selectors.findAll('.modal.show, .vg-modal.show')
606
+ .map((element) => Number.parseInt(window.getComputedStyle(element).zIndex, 10))
607
+ .filter((index) => Number.isFinite(index));
608
+
609
+ return String((indexes.length ? Math.max(...indexes) : 1040) + 10);
610
+ }
611
+
612
+ /**
613
+ * Закрытие всех открытых модальных окон
614
+ * @private
615
+ */
616
+ _closeOpenedModals() {
617
+ [...document.getElementsByClassName('modal')].forEach((element) => {
618
+ if (element && element.classList.contains('show')) {
619
+ if (typeof bootstrap !== 'undefined' && bootstrap.Modal?.getOrCreateInstance) {
620
+ const mBS = bootstrap.Modal.getOrCreateInstance(element);
621
+ mBS.hide();
622
+ } else {
623
+ console.warn(lang_messages(this._params.lang, NAME).bootstrap_not_found)
624
+ }
625
+ }
626
+ });
627
+
628
+ [...document.getElementsByClassName('vg-modal')].forEach((element) => {
629
+ if (element && element.classList.contains('show')) {
630
+ const mVG = VGModal.getOrCreateInstance(element);
631
+ mVG.hide([mVG]);
632
+ }
633
+ });
634
+ }
580
635
 
581
636
  /**
582
637
  * Показ алерта в виде collapse
@@ -614,13 +669,17 @@ class VGFormSender extends BaseModule {
614
669
  * @param {string} status - Статус (success/error)
615
670
  * @private
616
671
  */
617
- _alertToast(data, status) {
618
- const response = this._prepareAlertResponse(status, data);
619
- const toastParams = this._getToastParams(status);
620
-
621
- if (response.title) {
622
- VGToast.run([response.title, response.message], toastParams);
623
- return;
672
+ _alertToast(data, status) {
673
+ const response = this._prepareAlertResponse(status, data);
674
+ const toastParams = this._getToastParams(status);
675
+
676
+ if (this._params.alert.toast?.closeModalsBeforeToast) {
677
+ this._closeOpenedModals();
678
+ }
679
+
680
+ if (response.title) {
681
+ VGToast.run([response.title, response.message], toastParams);
682
+ return;
624
683
  }
625
684
 
626
685
  VGToast.run(response.message, toastParams);
@@ -634,9 +693,11 @@ class VGFormSender extends BaseModule {
634
693
  */
635
694
  _getToastParams(status) {
636
695
  const theme = status === 'error' ? 'danger' : status;
637
- const delay = this._params.alert.delay > 0 ? this._params.alert.delay : 3000;
638
- const flatToastParams = {};
639
- const allowedKeys = [
696
+ const toastType = status === 'danger' ? 'error' : status;
697
+ const delay = this._params.alert.delay > 0 ? this._params.alert.delay : 3000;
698
+ const flatToastParams = {};
699
+ const {closeModalsBeforeToast, ...toastParams} = this._params.alert.toast || {};
700
+ const allowedKeys = [
640
701
  'static',
641
702
  'placement',
642
703
  'autohide',
@@ -645,7 +706,6 @@ class VGFormSender extends BaseModule {
645
706
  'enableButtonClose',
646
707
  'keyboard',
647
708
  'theme',
648
- 'type',
649
709
  'drag',
650
710
  'resize',
651
711
  'stack',
@@ -659,13 +719,14 @@ class VGFormSender extends BaseModule {
659
719
  }
660
720
  });
661
721
 
662
- return mergeDeepObject({
663
- theme: theme || 'dark',
664
- enableButtonClose: true,
665
- autohide: this._params.alert.delay > 0,
666
- delay: delay
667
- }, flatToastParams, this._params.alert.toast || {});
668
- }
722
+ return mergeDeepObject({
723
+ theme: theme || 'dark',
724
+ type: toastType || null,
725
+ enableButtonClose: true,
726
+ autohide: this._params.alert.delay > 0,
727
+ delay: delay
728
+ }, flatToastParams, toastParams);
729
+ }
669
730
 
670
731
  /**
671
732
  * Формирование содержимого алерта (заголовок, текст, иконка)
@@ -771,10 +832,10 @@ class VGFormSender extends BaseModule {
771
832
  }, response.message);
772
833
  }
773
834
 
774
- return {
775
- title: response.title || '',
776
- message: message
777
- };
835
+ return {
836
+ title: this._params.alert.title ? response.title || '' : '',
837
+ message: message
838
+ };
778
839
  }
779
840
  }
780
841
 
@@ -873,3 +934,4 @@ EventHandler.on(document, EVENT_SUBMIT_DATA_API, function (event) {
873
934
  })
874
935
 
875
936
  export default VGFormSender;
937
+
@@ -20,9 +20,21 @@
20
20
  }
21
21
  }
22
22
 
23
- &--content {
24
- position: relative;
25
- }
23
+ &--content {
24
+ position: relative;
25
+
26
+ &-pass {
27
+ &:focus-within {
28
+ input {
29
+ padding-right: 5rem;
30
+ }
31
+
32
+ .vg-form-sender--hide-show-pass {
33
+ right: 44px;
34
+ }
35
+ }
36
+ }
37
+ }
26
38
 
27
39
  &--hide-show-pass {
28
40
  position: absolute;
@@ -40,12 +52,24 @@
40
52
  }
41
53
  }
42
54
 
43
- &.was-validated {
44
- .vg-form-sender--hide-show-pass {
45
- right: 34px;
46
- }
47
- }
48
- }
55
+ &.was-validated {
56
+ .vg-form-sender--hide-show-pass {
57
+ right: 34px;
58
+ }
59
+
60
+ .vg-form-sender--content-pass {
61
+ &:focus-within {
62
+ input {
63
+ padding-right: 6.25rem;
64
+ }
65
+
66
+ .vg-form-sender--hide-show-pass {
67
+ right: 64px;
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
49
73
 
50
74
  .vg-form-sender-alert {
51
75
  @include mix-alert-color-mode($class: vg-form-sender-alert);
@@ -102,4 +126,4 @@
102
126
  .vg-form-sender-alert-content--text {
103
127
  font-size: 1.25rem;
104
128
  color: var(--vg-form-sender-alert-color);
105
- }
129
+ }
@@ -289,13 +289,14 @@ class VGToast extends BaseModule {
289
289
 
290
290
  wrapper.append(content);
291
291
 
292
- // Кнопка закрытия
293
- if (params.enableButtonClose) {
294
- const button = document.createElement('div');
295
- button.classList.add('vg-toast-button');
296
- button.innerHTML = '<button class="vg-btn-close" data-vg-dismiss="toast"></button>';
297
- wrapper.append(button);
298
- }
292
+ // Кнопка закрытия
293
+ if (params.enableButtonClose) {
294
+ target.classList.add('vg-toast-has-button');
295
+ const button = document.createElement('div');
296
+ button.classList.add('vg-toast-button');
297
+ button.innerHTML = '<button class="vg-btn-close" data-vg-dismiss="toast"></button>';
298
+ wrapper.append(button);
299
+ }
299
300
 
300
301
  target.append(wrapper);
301
302
  document.body.append(target);