@formixru/b2b-ui 4.2.1 → 4.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formixru/b2b-ui",
3
- "version": "4.2.1",
3
+ "version": "4.3.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
package/src/base.scss CHANGED
@@ -28,7 +28,7 @@
28
28
 
29
29
  --input-delay: 300ms;
30
30
  --change-delay: 900ms;
31
- --loader-delay: 600ms;
31
+ --loader-delay: 500ms; // Значение подобрано так, чтобы при открытии .dialog/.sidebar не было мерцания
32
32
  --loader-delay-min: 300ms;
33
33
  --loader-min-time: 600ms;
34
34
  --fallback-timeout: 3000ms;
@@ -15,8 +15,9 @@ export const init = ($b) => {
15
15
  if (!mm('tablet-landscape-up')) {
16
16
  $b.prepend($b.querySelector('.dialog__close'));
17
17
  }
18
- // Отключаем скролл у body, если при загрузке на странице есть видимое диалоговое окно
19
18
  if (!$b.hidden) {
19
+ $b.classList.add('dialog_backdrop');
20
+ // Отключаем скролл у body, если при загрузке на странице есть видимое диалоговое окно
20
21
  disableScroll();
21
22
  $b.classList.toggle('dialog_overflow', isDialogOverflow($b));
22
23
  }
@@ -34,58 +35,76 @@ export const init = ($b) => {
34
35
  });
35
36
  };
36
37
 
37
- export const show = async ($b) => {
38
- if ($b?.hidden) {
39
- $b.dispatchEvent(new CustomEvent('dialog-show'));
38
+ export const show = async ($b, showBackdrop = 'before') => {
39
+ if (!$b?.hidden) {
40
+ return;
41
+ }
40
42
 
41
- disableScroll();
42
- $b.hidden = false;
43
+ $b.dispatchEvent(new CustomEvent('dialog-show'));
44
+
45
+ if (showBackdrop === 'before') {
46
+ $b.classList.add('dialog_backdrop');
47
+ }
43
48
 
44
- await Promise.all([
45
- $b.animate([{ opacity: 0 }, { opacity: 1 }], {
46
- duration: cssVar('--duration-enter-m'),
47
- easing: cssVar('--easing-deceleration'),
48
- }).finished,
49
+ disableScroll();
50
+ $b.hidden = false;
51
+
52
+ const promises = [
53
+ $b.animate([{ opacity: 0 }, { opacity: 1 }], {
54
+ duration: cssVar('--duration-enter-m'),
55
+ easing: cssVar('--easing-deceleration'),
56
+ }).finished,
57
+ ];
58
+ if (mm('tablet-landscape-up')) {
59
+ promises.push(
49
60
  $b
50
61
  .querySelector('.dialog__wrapper')
51
62
  .animate([{ transform: 'translateY(-15px)' }, { transform: 'translateY(0)' }], {
52
63
  duration: cssVar('--duration-enter-m'),
53
64
  easing: cssVar('--easing-deceleration'),
54
65
  }).finished,
55
- ]);
66
+ );
67
+ }
68
+ await Promise.all(promises);
56
69
 
57
- $b.dispatchEvent(new CustomEvent('dialog-shown'));
70
+ if (showBackdrop === 'after') {
71
+ $b.classList.add('dialog_backdrop');
58
72
  }
59
73
 
60
- return new Promise((resolve) => {
61
- resolve();
62
- });
74
+ $b.dispatchEvent(new CustomEvent('dialog-shown'));
63
75
  };
64
76
 
65
77
  export const hide = async ($b) => {
66
- if ($b && !$b.hidden) {
67
- $b.dispatchEvent(new CustomEvent('dialog-hide'));
68
-
69
- await Promise.all([
70
- $b.animate([{ opacity: 1 }, { opacity: 0 }], {
71
- duration: cssVar('--duration-leave-m'),
72
- easing: cssVar('--easing-acceleration'),
73
- }).finished,
78
+ if (!$b || $b.hidden) {
79
+ enableScroll();
80
+ return;
81
+ }
82
+
83
+ $b.dispatchEvent(new CustomEvent('dialog-hide'));
84
+
85
+ const promises = [
86
+ $b.animate([{ opacity: 1 }, { opacity: 0 }], {
87
+ duration: cssVar('--duration-leave-m'),
88
+ easing: cssVar('--easing-acceleration'),
89
+ }).finished,
90
+ ];
91
+ if (mm('tablet-landscape-up')) {
92
+ promises.push(
74
93
  $b
75
94
  .querySelector('.dialog__wrapper')
76
95
  .animate([{ transform: 'translateY(0)' }, { transform: 'translateY(-15px)' }], {
77
96
  duration: cssVar('--duration-leave-m'),
78
97
  easing: cssVar('--easing-acceleration'),
79
98
  }).finished,
80
- ]);
99
+ );
100
+ }
101
+ await Promise.all(promises);
81
102
 
82
- $b.hidden = true;
83
- enableScroll();
103
+ $b.hidden = true;
104
+ $b.classList.remove('dialog_overflow', 'dialog_backdrop');
105
+ enableScroll();
84
106
 
85
- $b.dispatchEvent(new CustomEvent('dialog-hidden'));
86
- } else {
87
- enableScroll();
88
- }
107
+ $b.dispatchEvent(new CustomEvent('dialog-hidden'));
89
108
  };
90
109
 
91
110
  export const get = async (id, url) => {
@@ -96,9 +115,9 @@ export const get = async (id, url) => {
96
115
  const $b = document.querySelector(elementId);
97
116
  if ($b) {
98
117
  $b.querySelector('.dialog__container').innerHTML = content;
99
- hideLoader();
100
- await show($b);
118
+ await show($b, 'after');
101
119
  initRequestContent($b);
120
+ hideLoader(0);
102
121
  } else {
103
122
  console.error(`Диалоговое окно с id ${elementId} не найдено`);
104
123
  }
@@ -3,7 +3,7 @@
3
3
  $b: '.dialog';
4
4
 
5
5
  #{$b} {
6
- background-color: $dialog-backdrop-color;
6
+ background-color: $dialog-bg;
7
7
  height: calc(100% - var(--tab-bar-height));
8
8
  left: 0;
9
9
  overflow: auto;
@@ -14,6 +14,7 @@ $b: '.dialog';
14
14
  z-index: 990;
15
15
 
16
16
  @media (--tablet-landscape-up) {
17
+ background-color: transparent;
17
18
  align-items: center;
18
19
  display: flex;
19
20
  flex-direction: column;
@@ -22,10 +23,6 @@ $b: '.dialog';
22
23
  padding: 4px * 10 0;
23
24
  }
24
25
 
25
- body:has(.loader_active) & {
26
- background-color: transparent;
27
- }
28
-
29
26
  &_overflow {
30
27
  @media (--tablet-landscape-up) {
31
28
  justify-content: flex-start;
@@ -41,18 +38,33 @@ $b: '.dialog';
41
38
  &_l {
42
39
  }
43
40
 
41
+ &_backdrop {
42
+ @media (--tablet-landscape-up) {
43
+ background-color: $dialog-backdrop-color;
44
+ }
45
+
46
+ // Т. к. .loader исчезает постепенно, с таким же эффектом показываем оверлей, чтобы не было мерцания
47
+ body:has(.loader_active) & {
48
+ @media (--tablet-landscape-up) {
49
+ transition-duration: var(--duration-leave-m);
50
+ transition-property: background-color;
51
+ transition-timing-function: var(--easing-standard);
52
+ }
53
+ }
54
+ }
55
+
44
56
  &__wrapper {
45
57
  background-color: $dialog-bg;
46
58
  color: $dialog-color;
47
59
  min-height: 100%;
48
- padding: 4px * 8 4px * 4;
60
+ padding: 32px 16px;
49
61
  width: 100%;
50
62
 
51
63
  @media (--tablet-landscape-up) {
52
64
  border-radius: $dialog-radius;
53
65
  box-shadow: $dialog-shadow;
54
66
  min-height: auto;
55
- padding: 4px * 10;
67
+ padding: 40px;
56
68
  position: relative;
57
69
  }
58
70
 
@@ -81,15 +93,15 @@ $b: '.dialog';
81
93
  color: $dialog-close-color;
82
94
  cursor: pointer;
83
95
  display: flex;
84
- height: 4px * 10;
96
+ height: 40px;
85
97
  justify-content: center;
86
98
  position: fixed;
87
- right: 4px * 2;
88
- top: 4px * 2;
99
+ right: 8px;
100
+ top: 8px;
89
101
  transition-duration: var(--duration-micro-s);
90
102
  transition-property: color;
91
103
  transition-timing-function: var(--easing-standard);
92
- width: 4px * 10;
104
+ width: 40px;
93
105
  z-index: 1;
94
106
 
95
107
  @media (--tablet-landscape-up) {
@@ -104,8 +116,8 @@ $b: '.dialog';
104
116
 
105
117
  &__close-icon {
106
118
  fill: currentcolor;
107
- height: 4px * 5;
108
- width: 4px * 5;
119
+ height: 20px;
120
+ width: 20px;
109
121
  }
110
122
 
111
123
  &__container {
@@ -120,18 +132,18 @@ $b: '.dialog';
120
132
  border: $dialog-header-image-border-width solid $dialog-header-image-border-color;
121
133
  border-radius: $dialog-radius;
122
134
  flex-shrink: 0;
123
- height: 4px * 20;
124
- margin-right: 4px * 6;
135
+ height: 80px;
136
+ margin-right: 24px;
125
137
  object-fit: contain;
126
- width: 4px * 20;
138
+ width: 80px;
127
139
 
128
140
  @media (--tablet-portrait-down) {
129
141
  display: none;
130
142
  }
131
143
 
132
144
  @media (--desktop-up) {
133
- height: 4px * 22;
134
- width: 4px * 22;
145
+ height: 88px;
146
+ width: 88px;
135
147
  }
136
148
  }
137
149
 
@@ -151,23 +163,23 @@ $b: '.dialog';
151
163
  }
152
164
 
153
165
  &__descr {
154
- margin-top: 4px * 6;
166
+ margin-top: 24px;
155
167
  max-width: 672px;
156
168
  }
157
169
 
158
170
  &__form {
159
- margin-top: 4px * 6;
171
+ margin-top: 24px;
160
172
  }
161
173
 
162
174
  &__product-table {
163
- margin-top: 4px * 6;
175
+ margin-top: 24px;
164
176
  }
165
177
 
166
178
  &__action-list {
167
179
  align-items: center;
168
180
  display: flex;
169
181
  flex-direction: column;
170
- margin-top: 4px * 6;
182
+ margin-top: 24px;
171
183
 
172
184
  @media (--tablet-landscape-up) {
173
185
  flex-direction: row;
@@ -181,14 +193,14 @@ $b: '.dialog';
181
193
  }
182
194
 
183
195
  &__action-item {
184
- max-width: 4px * 62;
196
+ max-width: 248px;
185
197
  white-space: normal;
186
198
 
187
199
  &:not(:first-child) {
188
- margin-top: 4px * 6;
200
+ margin-top: 24px;
189
201
 
190
202
  @media (--tablet-landscape-up) {
191
- margin-left: 4px * 6;
203
+ margin-left: 24px;
192
204
  margin-top: 0;
193
205
  }
194
206
  }
@@ -1,10 +1,12 @@
1
+ import { cssVar } from '@advdominion/css-var';
2
+
1
3
  let loaderTimeout;
2
4
 
3
5
  export const clearLoaderTimeout = () => {
4
6
  clearTimeout(loaderTimeout);
5
7
  };
6
8
 
7
- export const show = (delay) => {
9
+ export const show = (delayOverride) => {
8
10
  if (!document.querySelector('.loader')) {
9
11
  document.body.insertAdjacentHTML(
10
12
  'beforeend',
@@ -19,45 +21,60 @@ export const show = (delay) => {
19
21
  `,
20
22
  );
21
23
  }
24
+
22
25
  const $b = document.querySelector('.loader');
26
+ const delay = delayOverride ?? cssVar('--loader-delay');
23
27
  clearLoaderTimeout();
24
28
  return new Promise((resolve) => {
25
- loaderTimeout = setTimeout(
26
- () => {
27
- if (!$b.classList.contains('loader_active')) {
29
+ loaderTimeout = setTimeout(async () => {
30
+ if (!$b.classList.contains('loader_active')) {
31
+ $b.style.translate = 'none';
32
+ try {
33
+ await $b.animate([{ opacity: 0 }, { opacity: 1 }], {
34
+ duration: cssVar('--duration-enter-m'),
35
+ easing: cssVar('--easing-standard'),
36
+ }).finished;
28
37
  $b.classList.add('loader_active');
38
+ } catch {
39
+ // Анимация появления может прерваться в hide()
29
40
  }
30
- resolve();
31
- },
32
- delay === undefined ? window.__FORMIX__LOADER__DELAY || 500 : delay,
33
- );
41
+ }
42
+ resolve();
43
+ }, delay);
34
44
  });
35
45
  };
36
46
 
37
- export const hide = (delay) => {
47
+ export const hide = (delayOverride) => {
38
48
  const $b = document.querySelector('.loader');
49
+ const delay = delayOverride ?? cssVar('--loader-delay');
39
50
  clearLoaderTimeout();
40
51
  return new Promise((resolve) => {
41
- loaderTimeout = setTimeout(
42
- () => {
43
- if ($b.classList.contains('loader_active')) {
44
- $b.classList.remove('loader_active');
45
- }
46
- resolve();
47
- },
48
- delay === undefined ? window.__FORMIX__LOADER__DELAY || 500 : delay,
49
- );
50
- });
51
- };
52
+ loaderTimeout = setTimeout(async () => {
53
+ // Получаем текущее значение прозрачности, чтобы начать анимацию с него
54
+ const currentOpacity = window.getComputedStyle($b).opacity;
52
55
 
53
- window.showLoader = (delay) => {
54
- show(delay);
55
- };
56
+ for (const animation of $b.getAnimations()) {
57
+ animation.cancel();
58
+ }
59
+
60
+ // Если лоадер хоть немного виден, скрываем его из текущего значения
61
+ if (Number(currentOpacity) > 0) {
62
+ await $b.animate([{ opacity: currentOpacity }, { opacity: 0 }], {
63
+ duration: cssVar('--duration-leave-m'),
64
+ easing: cssVar('--easing-standard'),
65
+ }).finished;
66
+ }
56
67
 
57
- window.hideLoader = (delay) => {
58
- hide(delay);
68
+ $b.style.translate = '';
69
+ $b.classList.remove('loader_active');
70
+ resolve();
71
+ }, delay);
72
+ });
59
73
  };
60
74
 
75
+ window.showLoader = show;
76
+ window.hideLoader = hide;
77
+
61
78
  window.addEventListener('pageshow', (event) => {
62
79
  if (event.persisted && document.querySelector('.loader_active')) {
63
80
  hide(0);
@@ -27,18 +27,12 @@ $b: '.loader';
27
27
  opacity: 0;
28
28
  position: fixed;
29
29
  top: 0;
30
- transform: translateX(-100%);
31
- transition-duration: var(--duration-leave-s);
32
- transition-property: opacity;
33
- transition-timing-function: var(--easing-acceleration);
30
+ translate: -100%;
34
31
  width: 100%;
35
32
  z-index: 1000;
36
33
 
37
34
  &_active {
38
35
  opacity: 1;
39
- transform: translateX(0);
40
- transition-duration: var(--duration-enter-s);
41
- transition-timing-function: var(--easing-deceleration);
42
36
  }
43
37
 
44
38
  &__dots {
@@ -54,12 +48,12 @@ $b: '.loader';
54
48
  animation-timing-function: var(--easing-standard);
55
49
  background-color: $loader-color;
56
50
  border-radius: 50%;
57
- height: 4px * 6;
58
- width: 4px * 6;
51
+ height: 24px;
52
+ width: 24px;
59
53
 
60
54
  @media (--tablet-landscape-up) {
61
- height: 4px * 8;
62
- width: 4px * 8;
55
+ height: 32px;
56
+ width: 32px;
63
57
  }
64
58
 
65
59
  &_1 {
@@ -67,10 +61,10 @@ $b: '.loader';
67
61
 
68
62
  &_2 {
69
63
  animation-delay: 100ms;
70
- margin: 0 4px * 2;
64
+ margin: 0 8px;
71
65
 
72
66
  @media (--tablet-landscape-up) {
73
- margin: 0 4px * 3;
67
+ margin: 0 12px;
74
68
  }
75
69
  }
76
70
 
@@ -81,17 +75,17 @@ $b: '.loader';
81
75
 
82
76
  &__message {
83
77
  color: $loader-label-color;
84
- margin-left: 4px * 4;
85
- margin-right: 4px * 4;
86
- margin-top: 4px * 4;
78
+ margin-left: 16px;
79
+ margin-right: 16px;
80
+ margin-top: 16px;
87
81
  text-align: center;
88
82
 
89
83
  @include font($loader-label-mobile-font);
90
84
 
91
85
  @media (--tablet-landscape-up) {
92
- margin-left: 4px * 8;
93
- margin-right: 4px * 8;
94
- margin-top: 4px * 6;
86
+ margin-left: 32px;
87
+ margin-right: 32px;
88
+ margin-top: 24px;
95
89
 
96
90
  @include font($loader-label-desktop-font);
97
91
  }
@@ -37,18 +37,15 @@ const defaultBlockingSelectors = ['.dialog', '.sidebar'];
37
37
 
38
38
  export const disableScroll = () => {
39
39
  const scrollbarWidth = getScrollbarWidth();
40
- document.body.style = `
41
- overflow: hidden;
42
- margin-right: ${scrollbarWidth}px;
43
- `;
40
+
41
+ document.body.style.overflow = 'hidden';
42
+ document.body.style.marginRight = `${scrollbarWidth}px`;
44
43
 
45
44
  if (mm('desktop-up') && window.__FORMIX__OVERLAY__FIXED_ELEMENTS?.length > 0) {
46
45
  const fixedElements = [...document.querySelectorAll(window.__FORMIX__OVERLAY__FIXED_ELEMENTS.join(','))];
47
46
 
48
47
  for (const element of fixedElements) {
49
- element.style = `
50
- width: calc(100% - ${scrollbarWidth}px);
51
- `;
48
+ element.style.width = `calc(100% - ${scrollbarWidth}px)`;
52
49
  }
53
50
  }
54
51
  };
@@ -64,18 +61,14 @@ export const enableScroll = () => {
64
61
  const hasVisibleBlockingElements = blockingSelements.some((el) => !el.hidden);
65
62
 
66
63
  if (!hasVisibleBlockingElements) {
67
- document.body.style = `
68
- overflow: '';
69
- margin-right: '';
70
- `;
64
+ document.body.style.overflow = '';
65
+ document.body.style.marginRight = '';
71
66
 
72
67
  if (mm('desktop-up') && window.__FORMIX__OVERLAY__FIXED_ELEMENTS?.length > 0) {
73
68
  const fixedElements = [...document.querySelectorAll(window.__FORMIX__OVERLAY__FIXED_ELEMENTS.join(','))];
74
69
 
75
70
  for (const element of fixedElements) {
76
- element.style = `
77
- width: '';
78
- `;
71
+ element.style.width = '';
79
72
  }
80
73
  }
81
74
  }
@@ -14,17 +14,16 @@ $b: '.overlay';
14
14
  position: fixed;
15
15
  top: 0;
16
16
  transform: translateX(-100%);
17
- transition-duration: var(--duration-leave-s);
17
+ transition-duration: var(--duration-leave-m);
18
18
  transition-property: opacity;
19
- transition-timing-function: var(--easing-acceleration);
19
+ transition-timing-function: var(--easing-standard);
20
20
  width: 100%;
21
21
  z-index: var(--overlay-z-index);
22
22
 
23
23
  &_active {
24
24
  opacity: 1;
25
25
  transform: translateX(0);
26
- transition-duration: var(--duration-enter-s);
27
- transition-timing-function: var(--easing-deceleration);
26
+ transition-duration: var(--duration-enter-m);
28
27
  }
29
28
 
30
29
  body:has(.loader_active) & {
@@ -1,10 +1,11 @@
1
1
  /* globals BX */
2
2
 
3
+ import { cssVar } from '@advdominion/css-var';
3
4
  import { isString } from 'es-toolkit';
4
5
 
5
6
  import { errorHandler } from '../../utils/error-handler.js';
6
7
  import { hide as hideDialog, show as showDialog } from '../dialog/index.js';
7
- import { hide as hideLoader, show as showLoader } from '../loader/index.js';
8
+ import { clearLoaderTimeout, hide as hideLoader, show as showLoader } from '../loader/index.js';
8
9
  import { hide as hideSidebar, show as showSidebar } from '../sidebar/index.js';
9
10
 
10
11
  export const initRequestContent = ($b) => {
@@ -22,11 +23,20 @@ const getElementId = (id) => {
22
23
  return `#${id}`;
23
24
  };
24
25
 
26
+ export const redirect = (url) => {
27
+ if (!url || String(location.pathname + location.search) === url.replace(/#.*$/, '')) {
28
+ location.reload();
29
+ } else {
30
+ location.assign(url);
31
+ }
32
+ clearLoaderTimeout();
33
+ };
34
+
25
35
  export const request = async ({
26
36
  $b,
27
37
  url = '',
28
38
  method = 'GET',
29
- data = {},
39
+ data: requestData = {},
30
40
  signal,
31
41
  loader = true,
32
42
  showLoaderDelay,
@@ -35,6 +45,7 @@ export const request = async ({
35
45
  ...unknownParams
36
46
  }) => {
37
47
  let hideLoaderOnSuccess = initialHideLoaderOnSuccess;
48
+ let loaderDelay;
38
49
 
39
50
  const unknownKeys = Object.keys(unknownParams);
40
51
  if (unknownKeys.length > 0) {
@@ -57,21 +68,21 @@ export const request = async ({
57
68
 
58
69
  if (method === 'GET') {
59
70
  let entries;
60
- if (data instanceof FormData) {
61
- entries = data.entries();
62
- } else if (isString(data)) {
63
- entries = Object.entries(JSON.parse(data));
71
+ if (requestData instanceof FormData) {
72
+ entries = requestData.entries();
73
+ } else if (isString(requestData)) {
74
+ entries = Object.entries(JSON.parse(requestData));
64
75
  } else {
65
- entries = Object.entries(data);
76
+ entries = Object.entries(requestData);
66
77
  }
67
78
  for (const [name, value] of entries) {
68
79
  reqUrl.searchParams.append(name, value);
69
80
  }
70
81
  } else {
71
- if (data instanceof FormData || isString(data)) {
72
- options.body = data;
82
+ if (requestData instanceof FormData || isString(requestData)) {
83
+ options.body = requestData;
73
84
  } else {
74
- options.body = JSON.stringify(data);
85
+ options.body = JSON.stringify(requestData);
75
86
  options.headers['Content-Type'] = 'application/json';
76
87
  }
77
88
  }
@@ -87,133 +98,124 @@ export const request = async ({
87
98
  }
88
99
 
89
100
  const raw = await response.text();
90
- let data;
101
+ let responseData;
91
102
  try {
92
- data = JSON.parse(raw);
103
+ responseData = JSON.parse(raw);
93
104
  } catch {
94
105
  console.warn('Ошибка при попытке преобразования ответа в JSON');
95
- data = { content: raw };
106
+ responseData = { content: raw };
96
107
  }
97
108
 
98
- if (isString(data.redirect)) {
99
- if (data.redirect) {
100
- location.assign(data.redirect);
101
- } else {
102
- location.reload();
103
- }
109
+ if (
110
+ isString(responseData.redirect) &&
111
+ !responseData.redirectOnHideDialog &&
112
+ !responseData.redirectOnHideSidebar
113
+ ) {
114
+ redirect(responseData.redirect);
115
+ loaderDelay = cssVar('--fallback-timeout');
104
116
  } else {
105
117
  if (interceptData) {
106
118
  try {
107
- await window.__FORMIX__REQUEST__INTERCEPT_DATA(data);
119
+ await window.__FORMIX__REQUEST__INTERCEPT_DATA(responseData);
108
120
  } catch {
109
121
  console.warn('Функция __FORMIX__REQUEST__INTERCEPT_DATA не установлена');
110
122
  }
111
123
  }
112
124
 
113
- if (data.dialog) {
114
- const elementId = getElementId(data.dialog.id);
115
- const $dialog = document.querySelector(elementId);
125
+ const $dialog = responseData.dialog
126
+ ? document.querySelector(getElementId(responseData.dialog.id))
127
+ : undefined;
128
+ const $sidebar = responseData.sidebar
129
+ ? document.querySelector(getElementId(responseData.sidebar.id))
130
+ : undefined;
131
+
132
+ if (responseData.dialog) {
116
133
  if ($dialog) {
117
- $dialog.querySelector('.dialog__container').innerHTML = data.dialog.content;
118
- // Вызываем hideLoader так же как в основном компоненте - до функции show()
134
+ $dialog.querySelector('.dialog__container').innerHTML = responseData.dialog.content;
135
+ await showDialog($dialog, 'after');
136
+ initRequestContent($dialog);
119
137
  if (loader) {
120
- hideLoader();
138
+ hideLoader(0);
121
139
  hideLoaderOnSuccess = false;
122
140
  }
123
- await showDialog($dialog);
124
- initRequestContent($dialog);
125
141
  } else {
126
- console.warn(`Диалоговое окно с id ${elementId?.replace(/^#/, '')} не найдено`);
142
+ console.warn(`Диалоговое окно с id ${responseData.dialog.id?.replace(/^#/, '')} не найдено`);
127
143
  }
128
144
  }
129
145
 
130
- if (data.reloadOnHideDialog) {
131
- if (data.dialog) {
132
- const elementId = getElementId(data.dialog.id);
133
- const $dialog = document.querySelector(elementId);
134
- $dialog?.addEventListener('dialog-hide', () => {
135
- location.reload();
136
- });
137
- } else {
138
- $b?.closest('.dialog').addEventListener('dialog-hide', () => {
139
- location.reload();
140
- });
146
+ if (responseData.redirectOnHideDialog || responseData.reloadOnHideDialog) {
147
+ if (responseData.reloadOnHideDialog) {
148
+ console.warn(`Параметр reloadOnHideDialog - устаревший, используйте redirectOnHideDialog`);
141
149
  }
142
- }
143
150
 
144
- if (data.hideDialog) {
145
- if (data.dialog) {
146
- const elementId = getElementId(data.dialog.id);
147
- const $dialog = document.querySelector(elementId);
148
- if ($dialog) {
149
- hideDialog($dialog);
151
+ const $targetDialog = $dialog || $b?.closest('.dialog');
152
+ $targetDialog?.addEventListener('dialog-hide', () => {
153
+ if (responseData.redirectOnHideDialog) {
154
+ redirect(responseData.redirect);
155
+ } else {
156
+ location.reload();
150
157
  }
151
- } else if ($b) {
152
- hideDialog($b.closest('.dialog'));
158
+ });
159
+ }
160
+
161
+ if (responseData.hideDialog) {
162
+ const $targetDialog = $dialog || $b?.closest('.dialog');
163
+ if ($targetDialog) {
164
+ hideDialog($targetDialog);
153
165
  }
154
166
  }
155
167
 
156
- if (data.sidebar) {
157
- const elementId = getElementId(data.sidebar.id);
158
- const $sidebar = document.querySelector(elementId);
168
+ if (responseData.sidebar) {
159
169
  if ($sidebar) {
160
- $sidebar.innerHTML = data.sidebar.content;
161
- // Вызываем hideLoader так же как в основном компоненте - до функции show()
170
+ $sidebar.innerHTML = responseData.sidebar.content;
171
+ await showSidebar($sidebar, 'after');
172
+ initRequestContent($sidebar);
162
173
  if (loader) {
163
- hideLoader();
174
+ hideLoader(0);
164
175
  hideLoaderOnSuccess = false;
165
176
  }
166
- await showSidebar($sidebar);
167
- initRequestContent($sidebar);
168
177
  } else {
169
- console.warn(`Сайдбар с id ${elementId?.replace(/^#/, '')} не найден`);
178
+ console.warn(`Сайдбар с id ${responseData.sidebar.id?.replace(/^#/, '')} не найден`);
170
179
  }
171
180
  }
172
181
 
173
- if (data.reloadOnHideSidebar) {
174
- if (data.sidebar) {
175
- const elementId = getElementId(data.sidebar.id);
176
- const $sidebar = document.querySelector(elementId);
177
- $sidebar?.addEventListener('sidebar-hide', () => {
178
- location.reload();
179
- });
180
- } else {
181
- $b?.closest('.sidebar').addEventListener('sidebar-hide', () => {
182
- location.reload();
183
- });
182
+ if (responseData.redirectOnHideSidebar || responseData.reloadOnHideSidebar) {
183
+ if (responseData.reloadOnHideSidebar) {
184
+ console.warn(`Параметр reloadOnHideSidebar - устаревший, используйте redirectOnHideSidebar`);
184
185
  }
185
- }
186
186
 
187
- if (data.hideSidebar) {
188
- if (data.sidebar) {
189
- const elementId = getElementId(data.sidebar.id);
190
- const $sidebar = document.querySelector(elementId);
191
- if ($sidebar) {
192
- hideSidebar($sidebar);
187
+ const $targetSidebar = $sidebar || $b?.closest('.sidebar');
188
+ $targetSidebar?.addEventListener('sidebar-hide', () => {
189
+ if (responseData.redirectOnHideSidebar) {
190
+ redirect(responseData.redirect);
191
+ } else {
192
+ location.reload();
193
193
  }
194
- } else if ($b) {
195
- hideSidebar($b.closest('.sidebar'));
194
+ });
195
+ }
196
+
197
+ if (responseData.hideSidebar) {
198
+ const $targetSidebar = $sidebar || $b?.closest('.sidebar');
199
+ if ($targetSidebar) {
200
+ hideSidebar($targetSidebar);
196
201
  }
197
202
  }
198
203
 
199
- if (data.trigger) {
200
- const $trigger = document.querySelector(data.trigger);
204
+ if (responseData.trigger) {
205
+ const $trigger = document.querySelector(responseData.trigger);
201
206
  if ($trigger) {
202
207
  $trigger.click();
203
208
  } else {
204
- console.warn(`Элемент с селектором ${data.trigger} не найден`);
205
- if (loader) {
206
- hideLoader();
207
- }
209
+ console.warn(`Элемент с селектором ${responseData.trigger} не найден`);
208
210
  }
209
211
  }
210
212
  }
211
213
 
212
214
  if (loader && hideLoaderOnSuccess) {
213
- hideLoader(isString(data.redirect) ? 3000 : undefined);
215
+ hideLoader(loaderDelay);
214
216
  }
215
217
 
216
- return data;
218
+ return responseData;
217
219
  } catch (error) {
218
220
  if (error.status === 401) {
219
221
  location.reload();
@@ -2,64 +2,83 @@ import { cssVar } from '@advdominion/css-var';
2
2
 
3
3
  import { mm } from '../../utils/mm.js';
4
4
  import { hide as hideLoader } from '../loader/index.js';
5
- import { disableScroll, enableScroll, hide as hideOverlay, show as showOverlay } from '../overlay/index.js';
5
+ import { disableScroll, enableScroll } from '../overlay/index.js';
6
6
  import { initRequestContent, request } from '../request/index.js';
7
7
 
8
- export const show = async ($b) => {
9
- if ($b?.hidden) {
10
- $b.dispatchEvent(new CustomEvent('sidebar-show'));
11
-
8
+ const $sidebars = document.querySelectorAll('.sidebar');
9
+ for (const $b of $sidebars) {
10
+ if (!$b.hidden) {
11
+ $b.classList.add('sidebar_backdrop');
12
+ // Отключаем скролл у body, если при загрузке на странице есть открытый сайдбар
12
13
  if ($b.id !== 'settings-generator') {
13
14
  disableScroll();
14
15
  }
15
- $b.hidden = false;
16
- showOverlay();
17
-
18
- let width = mm('tablet-landscape-up')
19
- ? $b.offsetWidth + $b.querySelector('.sidebar__aside').offsetWidth
20
- : $b.offsetWidth;
21
- if ($b.classList.contains('sidebar_left')) {
22
- width = width * -1;
23
- }
24
- await $b.animate([{ transform: `translateX(${width}px)` }, { transform: 'translateX(0px)' }], {
25
- duration: cssVar('--duration-enter-m'),
26
- easing: cssVar('--easing-deceleration'),
27
- }).finished;
16
+ }
17
+ }
28
18
 
29
- $b.dispatchEvent(new CustomEvent('sidebar-shown'));
19
+ export const show = async ($b, showBackdrop = 'before') => {
20
+ if (!$b?.hidden) {
21
+ return;
30
22
  }
31
23
 
32
- return new Promise((resolve) => {
33
- resolve();
34
- });
35
- };
24
+ $b.dispatchEvent(new CustomEvent('sidebar-show'));
36
25
 
37
- export const hide = async ($b) => {
38
- if ($b && !$b.hidden) {
39
- $b.dispatchEvent(new CustomEvent('sidebar-hide'));
26
+ if (showBackdrop === 'before') {
27
+ $b.classList.add('sidebar_backdrop');
28
+ }
40
29
 
41
- hideOverlay();
30
+ if ($b.id !== 'settings-generator') {
31
+ disableScroll();
32
+ }
33
+ $b.hidden = false;
42
34
 
43
- let width = mm('tablet-landscape-up')
44
- ? $b.offsetWidth + $b.querySelector('.sidebar__aside').offsetWidth
45
- : $b.offsetWidth;
46
- if ($b.classList.contains('sidebar_left')) {
47
- width = width * -1;
48
- }
49
- await $b.animate([{ transform: 'translateX(0px)' }, { transform: `translateX(${width}px)` }], {
50
- duration: cssVar('--duration-leave-m'),
51
- easing: cssVar('--easing-acceleration'),
52
- }).finished;
35
+ let width = mm('tablet-landscape-up')
36
+ ? $b.offsetWidth + $b.querySelector('.sidebar__aside').offsetWidth
37
+ : $b.offsetWidth;
38
+ if ($b.classList.contains('sidebar_left')) {
39
+ width = width * -1;
40
+ }
41
+ await $b.animate([{ transform: `translateX(${width}px)` }, { transform: 'translateX(0px)' }], {
42
+ duration: cssVar('--duration-enter-m'),
43
+ easing: cssVar('--easing-deceleration'),
44
+ }).finished;
45
+
46
+ if (showBackdrop === 'after') {
47
+ $b.classList.add('sidebar_backdrop');
48
+ }
49
+
50
+ $b.dispatchEvent(new CustomEvent('sidebar-shown'));
51
+ };
53
52
 
54
- $b.hidden = true;
53
+ export const hide = async ($b) => {
54
+ if (!$b || $b.hidden) {
55
55
  if ($b.id !== 'settings-generator') {
56
56
  enableScroll();
57
57
  }
58
+ return;
59
+ }
60
+
61
+ $b.dispatchEvent(new CustomEvent('sidebar-hide'));
62
+
63
+ $b.classList.remove('sidebar_backdrop');
64
+
65
+ let width = mm('tablet-landscape-up')
66
+ ? $b.offsetWidth + $b.querySelector('.sidebar__aside').offsetWidth
67
+ : $b.offsetWidth;
68
+ if ($b.classList.contains('sidebar_left')) {
69
+ width = width * -1;
70
+ }
71
+ await $b.animate([{ transform: 'translateX(0px)' }, { transform: `translateX(${width}px)` }], {
72
+ duration: cssVar('--duration-leave-m'),
73
+ easing: cssVar('--easing-acceleration'),
74
+ }).finished;
58
75
 
59
- $b.dispatchEvent(new CustomEvent('sidebar-hidden'));
60
- } else if ($b.id !== 'settings-generator') {
76
+ $b.hidden = true;
77
+ if ($b.id !== 'settings-generator') {
61
78
  enableScroll();
62
79
  }
80
+
81
+ $b.dispatchEvent(new CustomEvent('sidebar-hidden'));
63
82
  };
64
83
 
65
84
  export const get = async (id, url, showLoaderDelay) => {
@@ -70,9 +89,9 @@ export const get = async (id, url, showLoaderDelay) => {
70
89
  const $b = document.querySelector(elementId);
71
90
  if ($b) {
72
91
  $b.innerHTML = content;
73
- hideLoader();
74
- await show($b);
92
+ await show($b, 'after');
75
93
  initRequestContent($b);
94
+ hideLoader(0);
76
95
  } else {
77
96
  console.error(`Сайдбар с id ${elementId} не найден`);
78
97
  }
@@ -83,17 +102,6 @@ export const get = async (id, url, showLoaderDelay) => {
83
102
  }
84
103
  };
85
104
 
86
- const $sidebars = document.querySelectorAll('.sidebar');
87
- for (const $el of $sidebars) {
88
- // Отключаем скролл у body, если при загрузке на странице есть открытый сайдбар
89
- if (!$el.hidden) {
90
- if ($el.id !== 'settings-generator') {
91
- disableScroll();
92
- }
93
- showOverlay();
94
- }
95
- }
96
-
97
105
  document.body.addEventListener('click', (event) => {
98
106
  const { target } = event;
99
107
  if (target.closest('[data-sidebar]')) {
@@ -3,33 +3,41 @@
3
3
  $b: '.sidebar';
4
4
 
5
5
  #{$b} {
6
- background-color: $sidebar-bg;
7
6
  height: calc(100% - var(--tab-bar-height));
8
7
  position: fixed;
9
- right: 0;
8
+ left: 0;
10
9
  top: 0;
11
- width: calc(100% - 56px);
10
+ width: 100%;
12
11
  z-index: 910;
12
+ display: flex;
13
+ justify-content: flex-end;
13
14
 
14
15
  @media (--tablet-landscape-up) {
15
16
  height: 100%;
16
- width: 320px;
17
17
  }
18
18
 
19
19
  &_left {
20
- left: 0;
21
- right: auto;
20
+ justify-content: flex-start;
22
21
  }
23
22
 
24
23
  &_l {
25
- @media (--tablet-landscape-up) {
26
- width: 640px;
27
- }
28
24
  }
29
25
 
30
26
  &_xl {
27
+ }
28
+
29
+ &_backdrop {
31
30
  @media (--tablet-landscape-up) {
32
- width: 960px;
31
+ background-color: $sidebar-backdrop-color;
32
+ }
33
+
34
+ // Т. к. .loader исчезает постепенно, с таким же эффектом показываем оверлей, чтобы не было мерцания
35
+ body:has(.loader_active) & {
36
+ @media (--tablet-landscape-up) {
37
+ transition-duration: var(--duration-leave-m);
38
+ transition-property: background-color;
39
+ transition-timing-function: var(--easing-standard);
40
+ }
33
41
  }
34
42
  }
35
43
 
@@ -41,10 +49,42 @@ $b: '.sidebar';
41
49
  right: 100%;
42
50
  top: 0;
43
51
 
52
+ @media (--tablet-landscape-up) {
53
+ right: 320px;
54
+ }
55
+
56
+ #{$b}_l & {
57
+ @media (--tablet-landscape-up) {
58
+ right: 640px;
59
+ }
60
+ }
61
+
62
+ #{$b}_xl & {
63
+ @media (--tablet-landscape-up) {
64
+ right: 960px;
65
+ }
66
+ }
67
+
44
68
  #{$b}_left & {
45
69
  align-items: flex-start;
46
70
  left: 100%;
47
71
  right: auto;
72
+
73
+ @media (--tablet-landscape-up) {
74
+ left: 320px;
75
+ }
76
+ }
77
+
78
+ #{$b}_left#{$b}_l & {
79
+ @media (--tablet-landscape-up) {
80
+ left: 640px;
81
+ }
82
+ }
83
+
84
+ #{$b}_left#{$b}_xl & {
85
+ @media (--tablet-landscape-up) {
86
+ left: 960px;
87
+ }
48
88
  }
49
89
  }
50
90
 
@@ -144,6 +184,24 @@ $b: '.sidebar';
144
184
  height: 100%;
145
185
  overflow: auto;
146
186
  -webkit-overflow-scrolling: touch;
187
+ width: calc(100% - 56px);
188
+ background-color: $sidebar-bg;
189
+
190
+ @media (--tablet-landscape-up) {
191
+ width: 320px;
192
+ }
193
+
194
+ #{$b}_l & {
195
+ @media (--tablet-landscape-up) {
196
+ width: 640px;
197
+ }
198
+ }
199
+
200
+ #{$b}_xl & {
201
+ @media (--tablet-landscape-up) {
202
+ width: 960px;
203
+ }
204
+ }
147
205
 
148
206
  #{$b}_catalog & {
149
207
  @media (--tablet-landscape-up) {
@@ -940,6 +940,7 @@ $dialog-title-desktop-font: ('font-size': $font-size-2xl) !default;
940
940
 
941
941
  // .sidebar
942
942
 
943
+ $sidebar-backdrop-color: $backdrop-color !default;
943
944
  $sidebar-bg: $bg-color-tertiary !default;
944
945
 
945
946
  $sidebar-button-bg: $color-grey-90 !default;