vgapp 1.2.1 → 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
@@ -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
  * Событие: успешная отправка формы
@@ -136,7 +137,12 @@ class VGFormSender extends BaseModule {
136
137
  errors: true,
137
138
  title: true,
138
139
  delay: 0,
139
- toast: {}
140
+ modal: {
141
+ closeModalsBeforeModal: true
142
+ },
143
+ toast: {
144
+ closeModalsBeforeToast: false
145
+ }
140
146
  },
141
147
  ajax: {
142
148
  route: '',
@@ -525,59 +531,107 @@ class VGFormSender extends BaseModule {
525
531
  * @param {string} status - Статус (success/error)
526
532
  * @private
527
533
  */
528
- _alertModal(data, status) {
529
- const _this = this;
530
-
531
- // Закрытие всех открытых модальных окон
532
- [...document.getElementsByClassName('modal')].forEach(function (element) {
533
- if (element && element.classList.contains('show')) {
534
- if (typeof bootstrap !== 'undefined') {
535
- let mBS = bootstrap.Modal?.getOrCreateInstance(element);
536
- mBS.hide();
537
- } else {
538
- console.warn(lang_messages(_this._params.lang, NAME).bootstrap_not_found)
539
- }
540
- }
541
- });
542
-
543
- [...document.getElementsByClassName('vg-modal')].forEach(function (element) {
544
- if (element && element.classList.contains('show')) {
545
- const mVG = VGModal.getOrCreateInstance(element);
546
- mVG.hide([mVG]);
547
- }
548
- });
549
-
550
- let id = _this._params.classes.general + '-' + makeRandomString(),
551
- $modal = Selectors.find('.' + _this._params.classes.alertModal);
552
-
553
- if ($modal) $modal.remove();
554
-
555
- setTimeout(() => {
556
- VGModal.init(id, {
557
- dismiss: true,
558
- classes: {
559
- alert: _this._params.classes.alertModal
560
- }
561
- }, function (self) {
562
- let element = self._element;
563
- element.classList.add(_this._params.classes.alertModal);
564
-
565
- let $content = Selectors.find('.vg-modal-content', element);
566
- if ($content) $content.classList.add(CLASS_NAME_ALERT, CLASS_NAME_ALERT + '-' + status);
567
-
568
- let $body = Selectors.find('.vg-modal-body', element);
569
- if ($body) $body.append(_this.setDataRelationStatus(element, status, data, 'modal'));
570
-
571
- self.toggle();
572
-
573
- if (_this._params.alert.delay > 0) {
574
- setTimeout(() => {
575
- self.hide();
576
- }, _this._params.alert.delay)
577
- }
578
- });
579
- }, _this._params.timeout);
580
- }
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
+ }
581
635
 
582
636
  /**
583
637
  * Показ алерта в виде collapse
@@ -615,13 +669,17 @@ class VGFormSender extends BaseModule {
615
669
  * @param {string} status - Статус (success/error)
616
670
  * @private
617
671
  */
618
- _alertToast(data, status) {
619
- const response = this._prepareAlertResponse(status, data);
620
- const toastParams = this._getToastParams(status);
621
-
622
- if (response.title) {
623
- VGToast.run([response.title, response.message], toastParams);
624
- 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;
625
683
  }
626
684
 
627
685
  VGToast.run(response.message, toastParams);
@@ -635,10 +693,11 @@ class VGFormSender extends BaseModule {
635
693
  */
636
694
  _getToastParams(status) {
637
695
  const theme = status === 'error' ? 'danger' : status;
638
- const toastType = status === 'danger' ? 'error' : status;
639
- const delay = this._params.alert.delay > 0 ? this._params.alert.delay : 3000;
640
- const flatToastParams = {};
641
- 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 = [
642
701
  'static',
643
702
  'placement',
644
703
  'autohide',
@@ -660,14 +719,14 @@ class VGFormSender extends BaseModule {
660
719
  }
661
720
  });
662
721
 
663
- return mergeDeepObject({
664
- theme: theme || 'dark',
665
- type: toastType || null,
666
- enableButtonClose: true,
667
- autohide: this._params.alert.delay > 0,
668
- delay: delay
669
- }, flatToastParams, this._params.alert.toast || {});
670
- }
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
+ }
671
730
 
672
731
  /**
673
732
  * Формирование содержимого алерта (заголовок, текст, иконка)
@@ -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
+ }