@formixru/b2b-ui 1.7.0 → 2.0.1

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/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## v2.0.1
2
+
3
+ - Исправлена работа `Request`
4
+ - Исправлена работа `cssVar`
5
+
6
+ ## v2.0.0
7
+
8
+ - Добавлен новый компонент - универсальный AJAX-контроллер `Request`, который используется внутри `Dialog`/`Form`/`Sidebar` и может использоваться как отдельный компонент
9
+ - Storybook обновлён до 9-й версии
10
+
1
11
  ## v1.7.0
2
12
 
3
13
  - Добавлен новый вид для WarningMessage (`light-info`)
package/CONTRIBUTING.md CHANGED
@@ -55,7 +55,5 @@ yarn dlx storybook@latest upgrade
55
55
 
56
56
  yarn upgrade-interactive
57
57
 
58
- yarn update-browserslist-db
59
-
60
58
  yarn dlx @yarnpkg/sdks
61
59
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formixru/b2b-ui",
3
- "version": "1.7.0",
3
+ "version": "2.0.1",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,7 +11,7 @@
11
11
  "defaults",
12
12
  "not OperaMini all"
13
13
  ],
14
- "packageManager": "yarn@4.6.0",
14
+ "packageManager": "yarn@4.9.3",
15
15
  "scripts": {
16
16
  "_postinstall": "husky",
17
17
  "prepack": "yarn pinst --disable",
@@ -26,58 +26,51 @@
26
26
  "devDependencies": {
27
27
  "@advdominion/get-scrollbar-width": "^2.0.0",
28
28
  "@advdominion/is": "^1.0.1",
29
- "@advdominion/lightningcss-loader": "^1.0.0",
30
29
  "@advdominion/pluralize": "^1.0.1",
31
- "@advdominion/tooltip": "^3.0.0",
32
- "@eslint/eslintrc": "^3.2.0",
33
- "@eslint/js": "^9.18.0",
34
- "@floating-ui/dom": "^1.6.13",
30
+ "@advdominion/tooltip": "^3.0.1",
31
+ "@eslint/eslintrc": "^3.3.1",
32
+ "@eslint/js": "^9.33.0",
33
+ "@floating-ui/dom": "^1.7.4",
35
34
  "@popperjs/core": "^2.11.8",
36
- "@prettier/plugin-php": "^0.22.4",
37
- "@storybook/addon-designs": "^8.0.4",
38
- "@storybook/addon-essentials": "^8.5.1",
39
- "@storybook/addon-links": "^8.5.1",
40
- "@storybook/addon-webpack5-compiler-swc": "^2.0.0",
41
- "@storybook/blocks": "^8.5.1",
42
- "@storybook/vue3": "^8.5.1",
43
- "@storybook/vue3-webpack5": "^8.5.1",
44
- "browserslist": "^4.24.4",
45
- "color": "^4.2.3",
46
- "css-loader": "^7.1.2",
35
+ "@prettier/plugin-php": "^0.24.0",
36
+ "@storybook/addon-designs": "^10.0.2",
37
+ "@storybook/addon-docs": "^9.1.3",
38
+ "@storybook/addon-links": "^9.1.3",
39
+ "@storybook/vue3-vite": "^9.1.3",
40
+ "@vitejs/plugin-vue": "^6.0.1",
41
+ "@vueless/storybook-dark-mode": "^9.0.7",
42
+ "color": "^5.0.0",
47
43
  "dayjs": "^1.11.13",
48
- "eslint": "^9.18.0",
49
- "eslint-config-prettier": "^10.0.1",
44
+ "eslint": "^9.33.0",
45
+ "eslint-config-prettier": "^10.1.8",
50
46
  "eslint-plugin-simple-import-sort": "^12.1.1",
51
- "eslint-plugin-unicorn": "^56.0.1",
52
- "eslint-plugin-vue": "^9.32.0",
53
- "globals": "^15.14.0",
47
+ "eslint-plugin-storybook": "9.1.3",
48
+ "eslint-plugin-unicorn": "^60.0.0",
49
+ "eslint-plugin-vue": "^10.4.0",
50
+ "globals": "^16.3.0",
54
51
  "husky": "^9.1.7",
55
52
  "imask": "^7.6.1",
56
- "lightningcss": "^1.29.1",
57
- "lint-staged": "^15.4.1",
53
+ "lint-staged": "^16.1.5",
58
54
  "lodash-es": "^4.17.21",
59
- "mime": "^4.0.6",
60
- "msw": "^2.7.0",
61
- "msw-storybook-addon": "^2.0.4",
55
+ "mime": "^4.0.7",
56
+ "msw": "^2.10.5",
57
+ "msw-storybook-addon": "^2.0.5",
62
58
  "nouislider": "^15.8.1",
63
59
  "pinst": "^3.0.0",
64
- "prettier": "^3.4.2",
60
+ "prettier": "^3.6.2",
65
61
  "reset-css": "^5.0.2",
66
- "sass-embedded": "^1.83.4",
67
- "sass-loader": "^16.0.4",
68
- "storybook": "^8.5.1",
69
- "storybook-dark-mode": "^4.0.2",
70
- "style-loader": "^4.0.0",
71
- "stylelint": "^16.13.2",
72
- "stylelint-config-standard-scss": "^14.0.0",
62
+ "sass-embedded": "^1.90.0",
63
+ "storybook": "^9.1.3",
64
+ "stylelint": "^16.23.1",
65
+ "stylelint-config-standard-scss": "^15.0.1",
73
66
  "stylelint-config-standard-vue": "^1.0.0",
74
- "stylelint-order": "^6.0.4",
75
- "swiper": "^11.2.1",
76
- "update-browserslist-db": "^1.1.2",
67
+ "stylelint-order": "^7.0.0",
68
+ "swiper": "^11.2.10",
77
69
  "v-calendar": "3.0.1",
78
- "vue": "^3.5.13",
79
- "webpack": "^5.97.1",
80
- "yargs": "^17.7.2"
70
+ "vite": "^7.1.3",
71
+ "vue": "^3.5.19",
72
+ "vue-eslint-parser": "^10.2.0",
73
+ "yargs": "^18.0.0"
81
74
  },
82
75
  "msw": {
83
76
  "workerDirectory": [
@@ -88,8 +81,8 @@
88
81
  "@advdominion/get-scrollbar-width": "^2.0.0",
89
82
  "@advdominion/is": "^1.0.0",
90
83
  "@advdominion/pluralize": "^1.0.0",
91
- "@advdominion/tooltip": "^1.0.0",
92
- "color": "^4.0.0",
84
+ "@advdominion/tooltip": "^3.0.0",
85
+ "color": "^5.0.0",
93
86
  "dayjs": "^1.0.0",
94
87
  "imask": "^7.0.0",
95
88
  "lodash-es": "^4.0.0",
@@ -40,7 +40,8 @@ $b: '.color-input';
40
40
  width: 4px * 7;
41
41
 
42
42
  &_transparent {
43
- background-image: linear-gradient(45deg, $color-grey-80 25%, transparent 25%),
43
+ background-image:
44
+ linear-gradient(45deg, $color-grey-80 25%, transparent 25%),
44
45
  linear-gradient(-45deg, $color-grey-80 25%, transparent 25%),
45
46
  linear-gradient(45deg, transparent 75%, $color-grey-80 75%),
46
47
  linear-gradient(-45deg, transparent 75%, $color-grey-80 75%);
@@ -1,13 +1,9 @@
1
- /* globals BX */
2
-
3
- import { is } from '@advdominion/is';
4
1
  import _throttle from 'lodash-es/throttle.js';
5
2
 
6
3
  import { cssVar } from '../../helpers/css-var.js';
7
- import { errorHandler } from '../../helpers/error-handler.js';
8
4
  import { mm } from '../../helpers/mm.js';
9
- import { clearLoaderTimeout, hide as hideLoader, show as showLoader } from '../loader/index.js';
10
5
  import { disableScroll, enableScroll } from '../overlay/index.js';
6
+ import { request } from '../request/index.js';
11
7
 
12
8
  const isDialogOverflow = ($b) => {
13
9
  return $b.scrollHeight > $b.clientHeight;
@@ -16,7 +12,6 @@ const isDialogOverflow = ($b) => {
16
12
  export const init = ($b) => {
17
13
  // Перемещаем кнопку закрытия модального окна для моб. устройств
18
14
  if (!mm('tablet-landscape-up')) {
19
- // $b.find('.dialog__close').prependTo($b); ??
20
15
  $b.prepend($b.querySelector('.dialog__close'));
21
16
  }
22
17
  // Отключаем скролл у body, если при загрузке на странице есть видимое диалоговое окно
@@ -47,7 +42,7 @@ export const init = ($b) => {
47
42
  };
48
43
 
49
44
  export const show = async ($b) => {
50
- if ($b && $b.hidden) {
45
+ if ($b?.hidden) {
51
46
  $b.dispatchEvent(new CustomEvent('dialog-show'));
52
47
 
53
48
  disableScroll();
@@ -109,52 +104,15 @@ export const initDialogContent = ($b) => {
109
104
 
110
105
  export const get = async (id, url) => {
111
106
  if (url && url !== '#') {
112
- showLoader();
113
- try {
114
- const response = await fetch(url, {
115
- method: 'GET',
116
- headers: {
117
- 'X-Requested-With': 'XMLHttpRequest',
118
- 'X-Session-Id': BX.message('bitrix_sessid'),
119
- },
120
- });
121
- if (!response.ok) {
122
- throw response;
123
- }
124
- let data = await response.text();
125
- try {
126
- data = JSON.parse(data);
127
- if (is(data.redirect, 'String')) {
128
- if (data.redirect) {
129
- location.assign(data.redirect);
130
- clearLoaderTimeout();
131
- } else {
132
- location.reload();
133
- }
134
- } else {
135
- try {
136
- await window.__FORMIX__DIALOG__INTERCEPT_DATA(data);
137
- } catch {
138
- console.warn('Функция __FORMIX__DIALOG__INTERCEPT_DATA не установлена');
139
- }
140
- data = data.content || '';
141
- }
142
- } catch {} // eslint-disable-line no-empty
143
- if (data.length > 0) {
144
- const $b = document.querySelector(id);
145
- $b.querySelector('.dialog__container').innerHTML = data;
107
+ const { content } = await request({ url });
108
+ if (content) {
109
+ const $b = document.querySelector(id);
110
+ if ($b) {
111
+ $b.querySelector('.dialog__container').innerHTML = content;
146
112
  await show($b);
147
- hideLoader();
148
113
  initDialogContent($b);
149
114
  } else {
150
- hideLoader();
151
- }
152
- } catch (error) {
153
- if (error.status === 401) {
154
- location.reload();
155
- } else {
156
- hideLoader();
157
- errorHandler(error);
115
+ console.error(`Диалоговое окно с id ${id} не найдено`);
158
116
  }
159
117
  }
160
118
  } else {
@@ -193,11 +151,7 @@ document.body.addEventListener('click', (event) => {
193
151
  if (target.closest('[data-dialog]')) {
194
152
  event.preventDefault();
195
153
  const $el = target.closest('[data-dialog]');
196
- let url = $el.dataset.dialogFetch || $el.href;
197
- if (url) {
198
- url = new URL(url, location.origin);
199
- }
200
- get($el.dataset.dialog, url);
154
+ get($el.dataset.dialog, $el.dataset.dialogFetch ?? $el.href);
201
155
  } else if (target.closest('[data-dialog-close]')) {
202
156
  hide(target.closest('[data-dialog-close]').closest('.dialog'));
203
157
  } else if (target.matches('.dialog') && target === $pointerDownTarget) {
@@ -1,12 +1,9 @@
1
- /* globals BX, SmartCaptcha, SMART_CAPTCHA_KEY */
1
+ /* globals SmartCaptcha, SMART_CAPTCHA_KEY */
2
2
 
3
- import { is } from '@advdominion/is';
4
-
5
- import { errorHandler } from '../../helpers/error-handler.js';
6
3
  import { getSmartCaptcha } from '../../helpers/get-smart-captcha.js';
7
4
  import { scrollTo } from '../../helpers/scroll-to.js';
8
- import { hide as hideDialog, initDialogContent, show as showDialog } from '../dialog/index.js';
9
- import { clearLoaderTimeout, hide as hideLoader, show as showLoader } from '../loader/index.js';
5
+ import { initDialogContent } from '../dialog/index.js';
6
+ import { request } from '../request/index.js';
10
7
 
11
8
  const initFormContent = ($b) => {
12
9
  try {
@@ -17,106 +14,34 @@ const initFormContent = ($b) => {
17
14
  };
18
15
 
19
16
  export const send = async ($b, url, method, data) => {
20
- url = new URL(url || $b.getAttribute('action'), location.origin);
21
- method = method || $b.getAttribute('method');
22
- data = data || new FormData($b);
23
- const options = {
24
- method,
25
- headers: {
26
- 'X-Requested-With': 'XMLHttpRequest',
27
- 'X-Session-Id': BX.message('bitrix_sessid'),
28
- },
29
- };
30
- if (method === 'GET') {
31
- for (const [name, value] of data) {
32
- url.searchParams.append(name, value);
33
- }
34
- } else {
35
- options.body = data;
17
+ const { success, content } = await request({
18
+ $b,
19
+ url: url || $b.getAttribute('action'),
20
+ method: method || $b.getAttribute('method'),
21
+ data: data || new FormData($b),
22
+ });
23
+ if (success) {
24
+ $b.dispatchEvent(new CustomEvent('form-success', { bubbles: true }));
36
25
  }
37
- showLoader();
38
- try {
39
- const response = await fetch(url, options);
40
- if (!response.ok) {
41
- throw response;
42
- }
43
- const data = await response.json().catch(() => {
44
- console.warn('Ошибка при попытке преобразования ответа в JSON.');
45
- return {};
46
- });
47
- if (is(data.redirect, 'String')) {
48
- if (data.redirect) {
49
- location.assign(data.redirect);
50
- if (data.redirect.startsWith('tg:')) {
51
- hideLoader();
52
- } else {
53
- clearLoaderTimeout();
54
- }
55
- } else {
56
- location.reload();
57
- }
26
+ if (content) {
27
+ const $dialog = $b.closest('.dialog');
28
+ if ($dialog) {
29
+ $dialog.querySelector('.dialog__container').innerHTML = content;
30
+ initDialogContent($dialog);
31
+ setTimeout(() => {
32
+ scrollToError($dialog);
33
+ });
58
34
  } else {
59
- if (!(data.reloadOnHideDialog && data.hideDialog) && (!data.trigger || data.hideDialog)) {
60
- hideLoader();
61
- }
62
- try {
63
- await window.__FORMIX__FORM__INTERCEPT_DATA(data);
64
- } catch {
65
- console.warn('Функция __FORMIX__FORM__INTERCEPT_DATA не установлена');
66
- }
67
- if (data.success) {
68
- $b.dispatchEvent(new CustomEvent('form-success', { bubbles: true }));
69
- }
70
- if (data.dialog) {
71
- const $dialog = document.querySelector(data.dialog.id);
72
- $dialog.querySelector('.dialog__container').innerHTML = data.dialog.content;
73
- showDialog($dialog);
74
- }
75
- if (data.reloadOnHideDialog) {
76
- $b.closest('.dialog').addEventListener('dialog-hide', () => {
77
- location.reload();
35
+ const $parent = $b.parentElement;
36
+ $b.outerHTML = content;
37
+ const $form = $parent.querySelector('.form');
38
+ if ($form) {
39
+ init($form);
40
+ initFormContent($form);
41
+ setTimeout(() => {
42
+ scrollToError($form);
78
43
  });
79
44
  }
80
- if (data.hideDialog) {
81
- hideDialog($b.closest('.dialog'));
82
- }
83
- if (data.trigger) {
84
- const $trigger = document.querySelector(data.trigger);
85
- if ($trigger) {
86
- $trigger?.click();
87
- console.error(`Элемент с селектором ${data.trigger} не найден`);
88
- } else {
89
- hideLoader();
90
- }
91
- }
92
- if (data.content) {
93
- const $dialog = $b.closest('.dialog');
94
- if ($dialog) {
95
- $dialog.querySelector('.dialog__container').innerHTML = data.content;
96
- initDialogContent($dialog);
97
- setTimeout(() => {
98
- scrollToError($dialog);
99
- });
100
- } else {
101
- const $parent = $b.parentElement;
102
- $b.outerHTML = data.content;
103
- const $form = $parent.querySelector('.form');
104
- if ($form) {
105
- init($form);
106
- initFormContent($form);
107
- setTimeout(() => {
108
- scrollToError($form);
109
- });
110
- }
111
- }
112
- }
113
- }
114
- } catch (error) {
115
- if (error.status === 401) {
116
- location.reload();
117
- } else {
118
- hideLoader();
119
- errorHandler(error);
120
45
  }
121
46
  }
122
47
  };
@@ -237,7 +237,7 @@ $b: '.form';
237
237
  &__value {
238
238
  color: $form-value-color;
239
239
  display: block;
240
- word-break: break-word;
240
+ overflow-wrap: break-word;
241
241
 
242
242
  @include font($form-value-font);
243
243
  }
@@ -33,7 +33,7 @@ export const init = async ($b) => {
33
33
  ];
34
34
 
35
35
  if (window.TEL_ADD_MASKS) {
36
- for (const item of [...window.TEL_ADD_MASKS].reverse()) {
36
+ for (const item of [...window.TEL_ADD_MASKS].toReversed()) {
37
37
  mask.unshift(item);
38
38
  }
39
39
  }
@@ -0,0 +1,168 @@
1
+ /* globals BX */
2
+
3
+ import { is } from '@advdominion/is';
4
+
5
+ import { errorHandler } from '../../helpers/error-handler.js';
6
+ import { hide as hideDialog, initDialogContent, show as showDialog } from '../dialog/index.js';
7
+ import { clearLoaderTimeout, hide as hideLoader, show as showLoader } from '../loader/index.js';
8
+ import { initSidebarContent, show as showSidebar } from '../sidebar/index.js';
9
+
10
+ export const request = async ({ $b, url = '', method = 'GET', data = {}, loader = true, showLoaderDelay }) => {
11
+ url = new URL(url, location.origin);
12
+ const options = {
13
+ method,
14
+ headers: {
15
+ 'X-Requested-With': 'XMLHttpRequest',
16
+ 'X-Session-Id': BX.message('bitrix_sessid'),
17
+ },
18
+ };
19
+ if (method === 'GET') {
20
+ let entries;
21
+ if (data instanceof FormData) {
22
+ entries = data.entries();
23
+ } else if (is(data, 'String')) {
24
+ entries = Object.entries(JSON.parse(data));
25
+ } else {
26
+ entries = Object.entries(data);
27
+ }
28
+ for (const [name, value] of entries) {
29
+ url.searchParams.append(name, value);
30
+ }
31
+ } else {
32
+ if (data instanceof FormData || is(data, 'String')) {
33
+ options.body = data;
34
+ } else {
35
+ options.body = JSON.stringify(data);
36
+ options.headers['Content-Type'] = 'application/json';
37
+ }
38
+ }
39
+ if (loader) {
40
+ showLoader(showLoaderDelay);
41
+ }
42
+ try {
43
+ const response = await fetch(url, options);
44
+ if (!response.ok) {
45
+ throw response;
46
+ }
47
+
48
+ const raw = await response.text();
49
+ let data;
50
+ try {
51
+ data = JSON.parse(raw);
52
+ } catch {
53
+ console.warn('Ошибка при попытке преобразования ответа в JSON');
54
+ data = { content: raw };
55
+ }
56
+
57
+ if (is(data.redirect, 'String')) {
58
+ if (data.redirect) {
59
+ location.assign(data.redirect);
60
+ if (data.redirect.startsWith('tg:')) {
61
+ if (loader) {
62
+ hideLoader();
63
+ }
64
+ } else {
65
+ clearLoaderTimeout();
66
+ }
67
+ } else {
68
+ location.reload();
69
+ }
70
+ } else {
71
+ try {
72
+ await window.__FORMIX__REQUEST__INTERCEPT_DATA(data);
73
+ } catch {
74
+ console.warn('Функция __FORMIX__REQUEST__INTERCEPT_DATA не установлена');
75
+ }
76
+ if (data.dialog) {
77
+ const $dialog = document.querySelector(data.dialog.id);
78
+ if ($dialog) {
79
+ $dialog.querySelector('.dialog__container').innerHTML = data.dialog.content;
80
+ await showDialog($dialog);
81
+ initDialogContent($dialog);
82
+ } else {
83
+ console.error(`Диалоговое окно с id ${data.trigger} не найдено`);
84
+ }
85
+ }
86
+ if (data.reloadOnHideDialog) {
87
+ if (data.dialog) {
88
+ const $dialog = document.querySelector(data.dialog.id);
89
+ $dialog?.addEventListener('dialog-hide', () => {
90
+ location.reload();
91
+ });
92
+ } else {
93
+ $b?.closest('.dialog').addEventListener('dialog-hide', () => {
94
+ location.reload();
95
+ });
96
+ }
97
+ }
98
+ if (data.hideDialog) {
99
+ if (data.dialog) {
100
+ const $dialog = document.querySelector(data.dialog.id);
101
+ if ($dialog) {
102
+ hideDialog($dialog);
103
+ }
104
+ } else if ($b) {
105
+ hideDialog($b.closest('.dialog'));
106
+ }
107
+ }
108
+ if (data.sidebar) {
109
+ const $sidebar = document.querySelector(data.sidebar.id);
110
+ if ($sidebar) {
111
+ $sidebar.innerHTML = data.sidebar.content;
112
+ await showSidebar($sidebar);
113
+ initSidebarContent($sidebar);
114
+ } else {
115
+ console.error(`Сайдбар с id ${data.trigger} не найден`);
116
+ }
117
+ }
118
+ if (data.trigger) {
119
+ const $trigger = document.querySelector(data.trigger);
120
+ if ($trigger) {
121
+ $trigger.click();
122
+ } else {
123
+ console.error(`Элемент с селектором ${data.trigger} не найден`);
124
+ if (loader) {
125
+ hideLoader();
126
+ }
127
+ }
128
+ }
129
+ if (!(data.reloadOnHideDialog && data.hideDialog) && (!data.trigger || data.hideDialog) && loader) {
130
+ hideLoader();
131
+ }
132
+ }
133
+
134
+ return data;
135
+ } catch (error) {
136
+ if (error.status === 401) {
137
+ location.reload();
138
+ } else {
139
+ if (loader) {
140
+ hideLoader();
141
+ }
142
+ errorHandler(error);
143
+ }
144
+ }
145
+ };
146
+
147
+ document.body.addEventListener('click', (event) => {
148
+ const { target } = event;
149
+ if (target.closest('[data-request]')) {
150
+ event.preventDefault();
151
+ const $el = target.closest('[data-request]');
152
+ const options = { $b: $el, url: $el.dataset.request ?? $el.href };
153
+ if ($el.dataset.requestMethod) {
154
+ options.method = $el.dataset.requestMethod;
155
+ }
156
+ if ($el.dataset.requestData) {
157
+ try {
158
+ options.method = JSON.parse($el.dataset.requestData);
159
+ } catch {
160
+ console.warn('Ошибка при попытке преобразования значения data-request-data в JSON');
161
+ }
162
+ }
163
+ if ($el.dataset.requestLoader === 'false') {
164
+ options.loader = false;
165
+ }
166
+ request(options);
167
+ }
168
+ });
@@ -1,12 +1,7 @@
1
- /* globals BX */
2
-
3
- import { is } from '@advdominion/is';
4
-
5
1
  import { cssVar } from '../../helpers/css-var.js';
6
- import { errorHandler } from '../../helpers/error-handler.js';
7
2
  import { mm } from '../../helpers/mm.js';
8
- import { clearLoaderTimeout, hide as hideLoader, show as showLoader } from '../loader/index.js';
9
3
  import { disableScroll, enableScroll, hide as hideOverlay, show as showOverlay } from '../overlay/index.js';
4
+ import { request } from '../request/index.js';
10
5
 
11
6
  export const show = async ($b) => {
12
7
  if ($b.id !== 'settings-generator') {
@@ -54,52 +49,15 @@ export const initSidebarContent = ($b) => {
54
49
 
55
50
  export const get = async (id, url, showLoaderDelay) => {
56
51
  if (url && url !== '#') {
57
- showLoader(showLoaderDelay);
58
- try {
59
- const response = await fetch(url, {
60
- method: 'GET',
61
- headers: {
62
- 'X-Requested-With': 'XMLHttpRequest',
63
- 'X-Session-Id': BX.message('bitrix_sessid'),
64
- },
65
- });
66
- if (!response.ok) {
67
- throw response;
68
- }
69
- let data = await response.text();
70
- try {
71
- data = JSON.parse(data);
72
- if (is(data.redirect, 'String')) {
73
- if (data.redirect) {
74
- location.assign(data.redirect);
75
- clearLoaderTimeout();
76
- } else {
77
- location.reload();
78
- }
79
- } else {
80
- try {
81
- await window.__FORMIX__SIDEBAR__INTERCEPT_DATA(data);
82
- } catch {
83
- console.warn('Функция __FORMIX__SIDEBAR__INTERCEPT_DATA не установлена');
84
- }
85
- data = data.content || '';
86
- }
87
- } catch {} // eslint-disable-line no-empty
88
- if (data.length > 0) {
89
- const $b = document.querySelector(id);
90
- $b.innerHTML = data;
52
+ const { content } = await request({ url, showLoaderDelay });
53
+ if (content) {
54
+ const $b = document.querySelector(id);
55
+ if ($b) {
56
+ $b.innerHTML = content;
91
57
  await show($b);
92
- hideLoader();
93
58
  initSidebarContent($b);
94
59
  } else {
95
- hideLoader();
96
- }
97
- } catch (error) {
98
- if (error.status === 401) {
99
- location.reload();
100
- } else {
101
- hideLoader();
102
- errorHandler(error);
60
+ console.error(`Сайдбар с id ${id} не найден`);
103
61
  }
104
62
  }
105
63
  } else {
@@ -119,29 +77,16 @@ for (const $el of $sidebars) {
119
77
  }
120
78
  }
121
79
 
122
- document.body.addEventListener('click', (e) => {
123
- const $el = e.target.closest('[data-sidebar]');
124
- if ($el) {
125
- e.preventDefault();
126
- let url = $el.dataset.sidebarFetch || $el.href;
127
- if (url) {
128
- url = new URL(url, location.origin);
129
- }
130
- get($el.dataset.sidebar, url);
131
- }
132
- });
133
-
134
- document.body.addEventListener('click', (e) => {
135
- const $b = e.target.closest('[data-sidebar-close]')?.closest('.sidebar');
136
- if ($b) {
137
- hide($b);
138
- }
139
- });
140
-
141
- document.body.addEventListener('click', (e) => {
142
- if (e.target.classList.contains('overlay')) {
143
- const $sidebars = document.querySelectorAll('.sidebar:not([hidden])');
144
- for (const $el of $sidebars) {
80
+ document.body.addEventListener('click', (event) => {
81
+ const { target } = event;
82
+ if (target.closest('[data-sidebar]')) {
83
+ event.preventDefault();
84
+ const $el = target.closest('[data-sidebar]');
85
+ get($el.dataset.sidebar, $el.dataset.sidebarFetch ?? $el.href);
86
+ } else if (target.closest('[data-sidebar-close]')) {
87
+ hide(target.closest('[data-sidebar-close]').closest('.sidebar'));
88
+ } else if (target.classList.contains('overlay')) {
89
+ for (const $el of document.querySelectorAll('.sidebar:not([hidden])')) {
145
90
  hide($el);
146
91
  }
147
92
  }
@@ -1,8 +1,9 @@
1
1
  export const cssVar = (name, element = document.documentElement) => {
2
2
  const value = window.getComputedStyle(element).getPropertyValue(name);
3
- const durationRegExp = new RegExp(/(^[\d.]+)s$/);
3
+ const durationRegExp = /(^[\d.]+)(ms|s)$/;
4
4
  if (durationRegExp.test(value)) {
5
- return durationRegExp.exec(value)[1] * 1000;
5
+ const [, numericValue, unit] = durationRegExp.exec(value);
6
+ return unit === 's' ? numericValue * 1000 : Number(numericValue);
6
7
  }
7
8
  return value;
8
9
  };
@@ -0,0 +1,126 @@
1
+ /* globals BX */
2
+
3
+ import { is } from '@advdominion/is';
4
+
5
+ import { hide as hideDialog, initDialogContent, show as showDialog } from '../components/dialog/index.js';
6
+ import { clearLoaderTimeout, hide as hideLoader, show as showLoader } from '../components/loader.js';
7
+ import { initSidebarContent, show as showSidebar } from '../components/sidebar/index.js';
8
+ import { errorHandler } from './error-handler.js';
9
+
10
+ export const req = async ({ $b, url = '', method = 'GET', data = {}, loader = true, showLoaderDelay }) => {
11
+ url = new URL(url, location.origin);
12
+ const options = {
13
+ method,
14
+ headers: {
15
+ 'X-Requested-With': 'XMLHttpRequest',
16
+ 'X-Session-Id': BX.message('bitrix_sessid'),
17
+ },
18
+ };
19
+ if (method === 'GET') {
20
+ for (const [name, value] of data) {
21
+ url.searchParams.append(name, value);
22
+ }
23
+ } else {
24
+ options.body = data;
25
+ }
26
+ if (loader) {
27
+ showLoader(showLoaderDelay);
28
+ }
29
+ try {
30
+ const response = await fetch(url, options);
31
+ if (!response.ok) {
32
+ throw response;
33
+ }
34
+ const data = await response.json().catch(async () => {
35
+ console.warn('Ошибка при попытке преобразования ответа в JSON.');
36
+ const content = await response.text();
37
+ return { content };
38
+ });
39
+
40
+ if (is(data.redirect, 'String')) {
41
+ if (data.redirect) {
42
+ location.assign(data.redirect);
43
+ if (data.redirect.startsWith('tg:')) {
44
+ if (loader) {
45
+ hideLoader();
46
+ }
47
+ } else {
48
+ clearLoaderTimeout();
49
+ }
50
+ } else {
51
+ location.reload();
52
+ }
53
+ } else {
54
+ try {
55
+ await window.__FORMIX__REQ__INTERCEPT_DATA(data);
56
+ } catch {
57
+ console.warn('Функция __FORMIX__REQUEST__INTERCEPT_DATA не установлена');
58
+ }
59
+ if (data.dialog) {
60
+ const $dialog = document.querySelector(data.dialog.id);
61
+ if ($dialog) {
62
+ $dialog.querySelector('.dialog__container').innerHTML = data.dialog.content;
63
+ await showDialog($dialog);
64
+ initDialogContent($dialog);
65
+ } else {
66
+ console.error(`Диалоговое окно с id ${data.trigger} не найдено`);
67
+ }
68
+ }
69
+ if (data.reloadOnHideDialog) {
70
+ if (data.dialog) {
71
+ const $dialog = document.querySelector(data.dialog.id);
72
+ $dialog?.addEventListener('dialog-hide', () => {
73
+ location.reload();
74
+ });
75
+ } else {
76
+ $b?.closest('.dialog').addEventListener('dialog-hide', () => {
77
+ location.reload();
78
+ });
79
+ }
80
+ }
81
+ if (data.hideDialog) {
82
+ if (data.dialog) {
83
+ const $dialog = document.querySelector(data.dialog.id);
84
+ if ($dialog) {
85
+ hideDialog($dialog);
86
+ }
87
+ } else if ($b) {
88
+ hideDialog($b.closest('.dialog'));
89
+ }
90
+ }
91
+ if (data.sidebar) {
92
+ const $sidebar = document.querySelector(data.sidebar.id);
93
+ if ($sidebar) {
94
+ $sidebar.innerHTML = data.sidebar.content;
95
+ await showSidebar($sidebar);
96
+ initSidebarContent($sidebar);
97
+ } else {
98
+ console.error(`Сайдбар с id ${data.trigger} не найден`);
99
+ }
100
+ }
101
+ if (data.trigger) {
102
+ const $trigger = document.querySelector(data.trigger);
103
+ if ($trigger) {
104
+ $trigger?.click();
105
+ console.error(`Элемент с селектором ${data.trigger} не найден`);
106
+ } else if (loader) {
107
+ hideLoader();
108
+ }
109
+ }
110
+ if (!(data.reloadOnHideDialog && data.hideDialog) && (!data.trigger || data.hideDialog) && loader) {
111
+ hideLoader();
112
+ }
113
+ }
114
+
115
+ return data;
116
+ } catch (error) {
117
+ if (error.status === 401) {
118
+ location.reload();
119
+ } else {
120
+ if (loader) {
121
+ hideLoader();
122
+ }
123
+ errorHandler(error);
124
+ }
125
+ }
126
+ };
package/vite.config.js ADDED
@@ -0,0 +1,75 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import path from 'node:path';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
+
6
+ import vue from '@vitejs/plugin-vue';
7
+ import { defineConfig } from 'vite';
8
+
9
+ const require = createRequire(import.meta.url);
10
+
11
+ export default defineConfig({
12
+ plugins: [
13
+ vue({
14
+ template: {
15
+ transformAssetUrls: {
16
+ video: [],
17
+ source: [],
18
+ img: [],
19
+ image: [],
20
+ use: [],
21
+ },
22
+ },
23
+ }),
24
+ ],
25
+ css: {
26
+ preprocessorOptions: {
27
+ scss: {
28
+ api: 'modern-compiler',
29
+ importers: [
30
+ {
31
+ canonicalize(url, context) {
32
+ if (url.startsWith('pkg:')) {
33
+ return pathToFileURL(require.resolve(url.replace('pkg:', '')));
34
+ }
35
+ // Обработка относительных путей внутри pkg:
36
+ if (context.containingUrl?.href.includes('node_modules')) {
37
+ const basePath = path.dirname(fileURLToPath(context.containingUrl));
38
+ const resolvedPath = path.resolve(basePath, url);
39
+
40
+ // Пробуем найти файл с нужным названием (проверка на пустую строку нужна для случаев, когда в URL уже стоит файл с расширением)
41
+ const extensions = ['', '.scss'];
42
+ for (const ext of extensions) {
43
+ const fullPath = resolvedPath + ext;
44
+ try {
45
+ if (readFileSync(fullPath)) {
46
+ return pathToFileURL(fullPath);
47
+ }
48
+ } catch {} // eslint-disable-line no-empty
49
+ }
50
+
51
+ // Если не нашли файл с нужным названием, пробуем найти index
52
+ for (const ext of ['.scss']) {
53
+ const indexPath = path.join(resolvedPath, `index${ext}`);
54
+ try {
55
+ if (readFileSync(indexPath)) {
56
+ return pathToFileURL(indexPath);
57
+ }
58
+ } catch {} // eslint-disable-line no-empty
59
+ }
60
+ }
61
+ // eslint-disable-next-line unicorn/no-null
62
+ return null;
63
+ },
64
+ load(canonicalUrl) {
65
+ return {
66
+ contents: readFileSync(fileURLToPath(canonicalUrl), 'utf8'),
67
+ syntax: 'scss',
68
+ };
69
+ },
70
+ },
71
+ ],
72
+ },
73
+ },
74
+ },
75
+ });