@formixru/b2b-ui 1.0.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.
Files changed (73) hide show
  1. package/CONTRIBUTING.md +61 -0
  2. package/README.md +30 -0
  3. package/package.json +110 -0
  4. package/src/base.scss +89 -0
  5. package/src/components/action-list/index.scss +38 -0
  6. package/src/components/banner/index.scss +30 -0
  7. package/src/components/breadcrumbs/index.js +8 -0
  8. package/src/components/breadcrumbs/index.scss +55 -0
  9. package/src/components/button/index.scss +223 -0
  10. package/src/components/checkbox/index.scss +94 -0
  11. package/src/components/color-input/index.scss +88 -0
  12. package/src/components/color-input/index.vue +156 -0
  13. package/src/components/date-input/index.scss +126 -0
  14. package/src/components/date-input/index.vue +240 -0
  15. package/src/components/delete-all/index.scss +44 -0
  16. package/src/components/dialog/index.js +221 -0
  17. package/src/components/dialog/index.scss +198 -0
  18. package/src/components/empty-list/index.scss +97 -0
  19. package/src/components/file-input/index.scss +119 -0
  20. package/src/components/file-input/index.vue +333 -0
  21. package/src/components/font-input/index.scss +47 -0
  22. package/src/components/font-input/index.vue +154 -0
  23. package/src/components/form/index.js +228 -0
  24. package/src/components/form/index.scss +398 -0
  25. package/src/components/header-announcement/index.scss +48 -0
  26. package/src/components/input/index.js +85 -0
  27. package/src/components/input/index.scss +87 -0
  28. package/src/components/link/index.scss +266 -0
  29. package/src/components/loader/index.js +65 -0
  30. package/src/components/loader/index.scss +99 -0
  31. package/src/components/nobr/index.scss +5 -0
  32. package/src/components/overlay/index.js +82 -0
  33. package/src/components/overlay/index.scss +29 -0
  34. package/src/components/pagination/index.scss +59 -0
  35. package/src/components/pagination-group/index.js +5 -0
  36. package/src/components/pagination-group/index.scss +86 -0
  37. package/src/components/quantity-input/index.scss +179 -0
  38. package/src/components/quantity-input/index.vue +141 -0
  39. package/src/components/radio/index.scss +80 -0
  40. package/src/components/radio-list/index.scss +79 -0
  41. package/src/components/range-input/index.scss +109 -0
  42. package/src/components/range-input/index.vue +185 -0
  43. package/src/components/rating-input/index.js +32 -0
  44. package/src/components/rating-input/index.scss +36 -0
  45. package/src/components/select/index.js +6 -0
  46. package/src/components/select/index.scss +84 -0
  47. package/src/components/sidebar/index.js +174 -0
  48. package/src/components/sidebar/index.scss +398 -0
  49. package/src/components/simple-page/index.scss +32 -0
  50. package/src/components/slider-nav/index.scss +82 -0
  51. package/src/components/slider-pagination/index.scss +130 -0
  52. package/src/components/spinner/index.scss +35 -0
  53. package/src/components/sr-only/index.scss +25 -0
  54. package/src/components/steps/index.scss +155 -0
  55. package/src/components/suggest-input/index.scss +141 -0
  56. package/src/components/suggest-input/index.vue +324 -0
  57. package/src/components/tabs/index.js +192 -0
  58. package/src/components/tabs/index.scss +137 -0
  59. package/src/components/time-select/index.scss +27 -0
  60. package/src/components/toggle/index.scss +76 -0
  61. package/src/components/tooltip/index.js +69 -0
  62. package/src/components/tooltip/index.scss +112 -0
  63. package/src/components/total/index.scss +162 -0
  64. package/src/components/warning-message/index.scss +170 -0
  65. package/src/components/warning-message/index.vue +94 -0
  66. package/src/components/wysiwyg/index.scss +344 -0
  67. package/src/helpers/css-var.js +8 -0
  68. package/src/helpers/error-handler.js +61 -0
  69. package/src/helpers/get-smart-captcha.js +7 -0
  70. package/src/helpers/is-in-viewport.js +10 -0
  71. package/src/helpers/mm.js +52 -0
  72. package/src/helpers/scroll-to.js +21 -0
  73. package/src/variables.scss +1295 -0
@@ -0,0 +1,109 @@
1
+ @use 'sass:math';
2
+ @use '../../variables' as *;
3
+
4
+ $b: '.range-input';
5
+
6
+ #{$b} {
7
+ &__track {
8
+ &.noUi-target {
9
+ background-color: $range-input-track-color;
10
+ border-bottom-style: none;
11
+ border-left-style: none;
12
+ border-radius: 0;
13
+ border-right-style: none;
14
+ border-top-style: none;
15
+ box-shadow: none;
16
+ cursor: pointer;
17
+ }
18
+
19
+ .noUi-base {
20
+ width: calc(100% - $range-input-handle-size);
21
+ }
22
+
23
+ .noUi-connects {
24
+ border-radius: 0;
25
+ }
26
+
27
+ .noUi-connect {
28
+ background-color: $range-input-connect-color;
29
+ }
30
+
31
+ .noUi-handle {
32
+ background-color: $range-input-handle-color;
33
+ border-bottom-style: none;
34
+ border-left-style: none;
35
+ border-radius: 10000px;
36
+ border-right-style: none;
37
+ border-top-style: none;
38
+ box-shadow: none;
39
+ transition-duration: var(--duration-micro-s);
40
+ transition-property: background-color;
41
+ transition-timing-function: var(--easing-standard);
42
+
43
+ &::before,
44
+ &::after {
45
+ content: none;
46
+ }
47
+
48
+ &:hover {
49
+ background-color: $range-input-handle-active-color;
50
+ }
51
+ }
52
+
53
+ &.noUi-horizontal {
54
+ align-items: center;
55
+ display: flex;
56
+ flex-direction: column;
57
+ height: $range-input-track-height;
58
+
59
+ .noUi-handle {
60
+ height: $range-input-handle-size;
61
+ top: math.div($range-input-handle-size - $range-input-track-height, 2) * -1;
62
+ width: $range-input-handle-size;
63
+
64
+ html:not([dir='rtl']) &-lower,
65
+ html:not([dir='rtl']) &-upper {
66
+ right: math.div($range-input-handle-size, 2) * -1;
67
+ }
68
+ }
69
+ }
70
+
71
+ &[disabled='true'] {
72
+ &.noUi-target {
73
+ background-color: $range-input-track-disabled-color;
74
+ }
75
+
76
+ .noUi-connect {
77
+ background-color: $range-input-connect-disabled-color;
78
+ }
79
+
80
+ .noUi-handle {
81
+ background-color: $range-input-handle-disabled-color;
82
+ }
83
+ }
84
+ }
85
+
86
+ &__wrapper {
87
+ display: flex;
88
+ justify-content: space-between;
89
+ margin-bottom: 24px;
90
+ position: relative;
91
+
92
+ &::before {
93
+ background-color: $range-input-separator-color;
94
+ content: '';
95
+ height: 1px;
96
+ left: 50%;
97
+ position: absolute;
98
+ top: 50%;
99
+ transform: translateX(-50%);
100
+ width: 1em;
101
+ }
102
+ }
103
+
104
+ &__input {
105
+ &.input {
106
+ width: calc(50% - (1em + 12px) / 2);
107
+ }
108
+ }
109
+ }
@@ -0,0 +1,185 @@
1
+ <template>
2
+ <div v-once ref="$root" class="range-input">
3
+ <div class="range-input__wrapper">
4
+ <input
5
+ ref="$from"
6
+ class="range-input__input input"
7
+ type="number"
8
+ :placeholder="`от ${initMin}`"
9
+ :min="initMin"
10
+ :value="initFromValue"
11
+ :step="initStep"
12
+ :name="fromName"
13
+ :disabled="initDisabled"
14
+ />
15
+ <input
16
+ ref="$to"
17
+ class="range-input__input input"
18
+ type="number"
19
+ :placeholder="`до ${initMax}`"
20
+ :max="initMax"
21
+ :value="initToValue"
22
+ :step="initStep"
23
+ :name="toName"
24
+ :disabled="initDisabled"
25
+ />
26
+ </div>
27
+ <div ref="$track" class="range-input__track"></div>
28
+ </div>
29
+ </template>
30
+
31
+ <script setup>
32
+ import noUiSlider from 'nouislider';
33
+ import { onMounted, ref, toRefs } from 'vue';
34
+
35
+ const emit = defineEmits(['input']);
36
+
37
+ const props = defineProps({
38
+ fromName: {
39
+ type: String,
40
+ default: '',
41
+ },
42
+ toName: {
43
+ type: String,
44
+ default: '',
45
+ },
46
+ initFromValue: {
47
+ type: Number,
48
+ default: undefined,
49
+ },
50
+ initToValue: {
51
+ type: Number,
52
+ default: undefined,
53
+ },
54
+ initMin: {
55
+ type: Number,
56
+ required: true,
57
+ },
58
+ initMax: {
59
+ type: Number,
60
+ required: true,
61
+ },
62
+ initStep: {
63
+ type: Number,
64
+ default: 1,
65
+ },
66
+ initDisabled: {
67
+ type: Boolean,
68
+ },
69
+ throttle: {
70
+ type: Number,
71
+ default: 1000,
72
+ },
73
+ });
74
+
75
+ const { fromName, toName, initFromValue, initToValue, initMin, initMax, initStep, initDisabled, throttle } =
76
+ toRefs(props);
77
+
78
+ const $root = ref();
79
+ const $track = ref();
80
+ const $from = ref();
81
+ const $to = ref();
82
+
83
+ onMounted(() => {
84
+ const setTrackValue = () => {
85
+ const min = Number($from.value.min);
86
+ const max = Number($to.value.max);
87
+
88
+ let from;
89
+ let to;
90
+
91
+ // Иначе, если мин. и макс. одинаковые, ползунок перемещается в конец трека
92
+ if (min !== max) {
93
+ from = $from.value.value.length > 0 ? Number($from.value.value) : min;
94
+ to = $to.value.value.length > 0 ? Number($to.value.value) : max;
95
+ }
96
+
97
+ $track.value.noUiSlider.set([from, to]);
98
+ };
99
+
100
+ const setFromTo = () => {
101
+ const value = $track.value.noUiSlider.get();
102
+ if ($from.value.value.length > 0 || Number(value[0]) > Number($from.value.min)) {
103
+ $from.value.value = Number(value[0]);
104
+ }
105
+ if ($to.value.value.length > 0 || Number(value[1]) < Number($to.value.max)) {
106
+ $to.value.value = Number(value[1]);
107
+ }
108
+ };
109
+
110
+ noUiSlider.create($track.value, {
111
+ start: [
112
+ initFromValue.value === undefined ? initMin.value : initFromValue.value,
113
+ initToValue.value === undefined ? initMax.value : initToValue.value,
114
+ ],
115
+ step: initStep.value,
116
+ range: {
117
+ min: [initMin.value],
118
+ max: [initMax.value],
119
+ },
120
+ connect: true,
121
+ });
122
+
123
+ if (initDisabled.value) {
124
+ $track.value.noUiSlider.target.setAttribute('disabled', true);
125
+ }
126
+
127
+ $track.value.noUiSlider.on('slide', () => {
128
+ setFromTo();
129
+ });
130
+
131
+ $track.value.noUiSlider.on('change', () => {
132
+ emit('input');
133
+ });
134
+
135
+ let throttleTimeout;
136
+
137
+ for (const $el of [$from.value, $to.value]) {
138
+ // Обновляем параметры ползунка при изменении атрибутов полей
139
+ const observer = new MutationObserver((mutations) => {
140
+ for (const { attributeName } of mutations) {
141
+ if (attributeName === 'step') {
142
+ $track.value.noUiSlider.updateOptions({
143
+ step: Number($el.step),
144
+ });
145
+ } else if (attributeName === 'disabled') {
146
+ if ($el.disabled) {
147
+ $track.value.noUiSlider.target.setAttribute('disabled', true);
148
+ } else {
149
+ $track.value.noUiSlider.target.removeAttribute('disabled');
150
+ }
151
+ } else {
152
+ $track.value.noUiSlider.updateOptions({
153
+ range: {
154
+ min: [Number($from.value.min)],
155
+ max: [Number($to.value.max)],
156
+ },
157
+ });
158
+ setTrackValue();
159
+ // Обновляем подсказки от/до
160
+ $from.value.placeholder = `от ${$from.value.min}`;
161
+ $to.value.placeholder = `до ${$to.value.max}`;
162
+ }
163
+ }
164
+ });
165
+ observer.observe($el, {
166
+ attributes: true,
167
+ attributeFilter: ['min', 'max', 'step', 'disabled'],
168
+ });
169
+
170
+ $el.addEventListener('input', () => {
171
+ setTrackValue();
172
+ const $filterItem = $root.value.closest('.filter__item');
173
+ $filterItem._tooltip?.hide();
174
+ clearTimeout(throttleTimeout);
175
+ throttleTimeout = setTimeout(() => {
176
+ emit('input');
177
+ }, throttle.value);
178
+ });
179
+
180
+ $el.addEventListener('change', () => {
181
+ setFromTo();
182
+ });
183
+ }
184
+ });
185
+ </script>
@@ -0,0 +1,32 @@
1
+ export const init = ($b) => {
2
+ const $stars = $b.querySelectorAll('.rating-input__star');
3
+
4
+ for (const $star of $stars) {
5
+ $star.addEventListener('mouseenter', (event) => {
6
+ const $active = event.target;
7
+ const activeIndex = [...$active.parentNode.children].indexOf($active);
8
+ for (const [index, $el] of Object.entries($stars)) {
9
+ $el.classList.toggle('rating-input__star_active', Number(index) <= activeIndex);
10
+ }
11
+ });
12
+
13
+ $star.addEventListener('mouseleave', (event) => {
14
+ if (
15
+ !event.relatedTarget.classList.contains('rating-input') &&
16
+ !event.relatedTarget.closest('.related-input')
17
+ ) {
18
+ const $active = $b.querySelector('.rating-input__star-input:checked');
19
+ if ($active) {
20
+ const activeIndex = [...$active.closest('.rating-input__star').parentNode.children].indexOf(
21
+ $active.closest('.rating-input__star'),
22
+ );
23
+ for (const [index, $el] of Object.entries($stars)) {
24
+ $el.classList.toggle('rating-input__star_active', Number(index) <= activeIndex);
25
+ }
26
+ } else {
27
+ $star.classList.remove('rating-input__star_active');
28
+ }
29
+ }
30
+ });
31
+ }
32
+ };
@@ -0,0 +1,36 @@
1
+ @use '../../variables' as *;
2
+
3
+ $b: '.rating-input';
4
+
5
+ #{$b} {
6
+ align-items: center;
7
+ display: inline-flex;
8
+ margin-left: -2px;
9
+ margin-right: -2px;
10
+
11
+ &__star {
12
+ cursor: pointer;
13
+ padding-left: 2px;
14
+ padding-right: 2px;
15
+
16
+ &_active {
17
+ }
18
+ }
19
+
20
+ &__star-input {
21
+ }
22
+
23
+ &__star-icon {
24
+ fill: $rating-input-star-icon-color;
25
+ height: 4px * 4;
26
+ transition-duration: var(--duration-micro-s);
27
+ transition-property: fill;
28
+ transition-timing-function: var(--easing-standard);
29
+ width: 4px * 4;
30
+
31
+ #{$b}__star:hover &,
32
+ #{$b}__star_active & {
33
+ fill: $rating-input-star-icon-active-color;
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,6 @@
1
+ document.body.addEventListener('change', (event) => {
2
+ const $select = event.target.closest('.select');
3
+ if ($select && $select.classList.contains('select_error')) {
4
+ $select.classList.remove('select_error');
5
+ }
6
+ });
@@ -0,0 +1,84 @@
1
+ @use 'sass:math';
2
+ @use '../../variables' as *;
3
+
4
+ // https://www.filamentgroup.com/lab/select-css.html
5
+
6
+ $b: '.select';
7
+
8
+ #{$b} {
9
+ appearance: none;
10
+ background-color: $select-bg;
11
+ background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23888888%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E');
12
+ background-position: right $select-padding top 50%;
13
+ background-repeat: no-repeat;
14
+ background-size: 6px auto;
15
+ border: $select-border-width solid $select-border-color;
16
+ border-radius: $select-radius;
17
+ color: $select-color;
18
+ cursor: pointer;
19
+ display: block;
20
+ height: $select-height;
21
+ padding-left: $select-padding;
22
+ padding-right: $select-padding * 2 + 6px;
23
+ transition-duration: var(--duration-micro-s);
24
+ transition-property: background-color, border-color, color;
25
+ transition-timing-function: var(--easing-standard);
26
+ width: 100%;
27
+
28
+ @include font($select-font);
29
+
30
+ &[multiple] {
31
+ background-image: none;
32
+ height: auto;
33
+ padding: 0;
34
+
35
+ option {
36
+ padding: math.div($select-padding, 2) $select-padding;
37
+ }
38
+ }
39
+
40
+ &::-ms-expand {
41
+ display: none;
42
+ }
43
+
44
+ &:disabled {
45
+ background-color: $select-disabled-bg;
46
+ border-color: $select-disabled-border-color;
47
+ color: $select-disabled-color;
48
+ cursor: default;
49
+ }
50
+
51
+ &:focus-visible {
52
+ background-color: $select-active-bg;
53
+ border-color: $select-active-border-color;
54
+ color: $select-active-color;
55
+ }
56
+
57
+ &:invalid {
58
+ color: $select-placeholder-color;
59
+ }
60
+
61
+ &_small {
62
+ height: $select-small-height;
63
+ padding-left: $select-small-padding;
64
+ padding-right: $select-small-padding * 2 + 6px;
65
+
66
+ @include font($select-small-font);
67
+
68
+ &[multiple] {
69
+ option {
70
+ padding: math.div($select-small-padding, 2) $select-small-padding;
71
+ }
72
+ }
73
+ }
74
+
75
+ &_error {
76
+ background-color: $select-error-bg;
77
+ border-color: $select-error-border-color;
78
+ color: $select-error-color;
79
+
80
+ &:focus-visible {
81
+ border-color: $select-error-border-color;
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,174 @@
1
+ /* globals BX */
2
+
3
+ import { is } from '@advdominion/is';
4
+
5
+ import { cssVar } from '../../helpers/css-var.js';
6
+ import { errorHandler } from '../../helpers/error-handler.js';
7
+ import { mm } from '../../helpers/mm.js';
8
+ import { clearLoaderTimeout, hide as hideLoader, show as showLoader } from '../loader/index.js';
9
+ import { disableScroll, enableScroll, hide as hideOverlay, show as showOverlay } from '../overlay/index.js';
10
+
11
+ export const show = async ($b) => {
12
+ if ($b.id !== 'settings-generator') {
13
+ disableScroll();
14
+ }
15
+ $b.hidden = false;
16
+ showOverlay();
17
+ let width = mm('tablet-landscape-up')
18
+ ? $b.offsetWidth + $b.querySelector('.sidebar__aside').offsetWidth
19
+ : $b.offsetWidth;
20
+ if ($b.classList.contains('sidebar_left')) {
21
+ width = width * -1;
22
+ }
23
+ await $b.animate([{ transform: `translateX(${width}px)` }, { transform: 'translateX(0px)' }], {
24
+ duration: cssVar('--duration-enter-m'),
25
+ easing: cssVar('--easing-deceleration'),
26
+ }).finished;
27
+ };
28
+
29
+ export const hide = async ($b) => {
30
+ hideOverlay();
31
+ let width = mm('tablet-landscape-up')
32
+ ? $b.offsetWidth + $b.querySelector('.sidebar__aside').offsetWidth
33
+ : $b.offsetWidth;
34
+ if ($b.classList.contains('sidebar_left')) {
35
+ width = width * -1;
36
+ }
37
+ await $b.animate([{ transform: 'translateX(0px)' }, { transform: `translateX(${width}px)` }], {
38
+ duration: cssVar('--duration-leave-m'),
39
+ easing: cssVar('--easing-acceleration'),
40
+ }).finished;
41
+ $b.hidden = true;
42
+ if ($b.id !== 'settings-generator') {
43
+ enableScroll();
44
+ }
45
+ };
46
+
47
+ export const initSidebarContent = ($b) => {
48
+ try {
49
+ window.__FORMIX__SIDEBAR__INIT_CONTENT($b);
50
+ } catch {
51
+ console.warn('Функция __FORMIX__SIDEBAR__INIT_CONTENT не установлена');
52
+ }
53
+ };
54
+
55
+ export const get = async (id, url, showLoaderDelay) => {
56
+ 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;
91
+ show($b).then(() => {
92
+ hideLoader();
93
+ initSidebarContent($b);
94
+ });
95
+ } else {
96
+ hideLoader();
97
+ }
98
+ } catch (error) {
99
+ if (error.status === 401) {
100
+ location.reload();
101
+ } else {
102
+ hideLoader();
103
+ errorHandler(error);
104
+ }
105
+ }
106
+ } else {
107
+ const $b = document.querySelector(id);
108
+ show($b);
109
+ }
110
+ };
111
+
112
+ const $sidebars = document.querySelectorAll('.sidebar');
113
+ for (const $el of $sidebars) {
114
+ // Отключаем скролл у body, если при загрузке на странице есть открытый сайдбар
115
+ if (!$el.hidden) {
116
+ if ($el.id !== 'settings-generator') {
117
+ disableScroll();
118
+ }
119
+ showOverlay();
120
+ }
121
+ }
122
+
123
+ document.body.addEventListener('click', (e) => {
124
+ const $el = e.target.closest('[data-sidebar]');
125
+ if ($el) {
126
+ e.preventDefault();
127
+ let url = $el.dataset.sidebarFetch || $el.href;
128
+ if (url) {
129
+ url = new URL(url, location.origin);
130
+ }
131
+ get($el.dataset.sidebar, url);
132
+ }
133
+ });
134
+
135
+ document.body.addEventListener('click', (e) => {
136
+ const $b = e.target.closest('[data-sidebar-close]')?.closest('.sidebar');
137
+ if ($b) {
138
+ hide($b);
139
+ }
140
+ });
141
+
142
+ document.body.addEventListener('click', (e) => {
143
+ if (e.target.classList.contains('overlay')) {
144
+ const $sidebars = document.querySelectorAll('.sidebar:not([hidden])');
145
+ for (const $el of $sidebars) {
146
+ hide($el);
147
+ }
148
+ }
149
+ });
150
+
151
+ document.body.addEventListener('keyup', (e) => {
152
+ if (e.key === 'Escape') {
153
+ const $sidebars = document.querySelectorAll('.sidebar:not([hidden])');
154
+ for (const $el of $sidebars) {
155
+ hide($el);
156
+ }
157
+ }
158
+ });
159
+
160
+ document.body.addEventListener('click', (e) => {
161
+ const $el = e.target.closest('button.sidebar__tab');
162
+ if ($el) {
163
+ const $b = $el.closest('.sidebar');
164
+ const $tabs = $b.querySelectorAll('.sidebar__tab');
165
+ const index = [...$tabs].indexOf($el);
166
+ for (const [i, $tab] of $tabs.entries()) {
167
+ $tab.disabled = i === index;
168
+ }
169
+ const $containers = $b.querySelectorAll('.sidebar__container');
170
+ for (const [i, $container] of $containers.entries()) {
171
+ $container.hidden = i !== index;
172
+ }
173
+ }
174
+ });