@webitel/ui-sdk 2.0.32-3 → 2.1.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.
@@ -1,41 +1,82 @@
1
1
  <template>
2
2
  <div
3
- class="wt-tags-input"
4
3
  :class="{
5
4
  'wt-tags-input--disabled': disabled,
6
5
  'wt-tags-input--invalid': invalid,
6
+ 'wt-tags-input--loading': isLoading,
7
7
  }"
8
+ class="wt-tags-input"
8
9
  >
9
10
  <wt-label
10
- class="wt-tags-input__label"
11
11
  v-if="hasLabel"
12
12
  :disabled="disabled"
13
13
  :invalid="invalid"
14
+ class="wt-tags-input__label"
14
15
  v-bind="labelProps"
15
16
  >
16
17
  <!-- @slot Custom input label -->
17
18
  <slot name="label" v-bind="{ label }">{{ requiredLabel }}</slot>
18
19
  </wt-label>
19
20
 
20
- <vue-tags-input
21
- class="wt-tags-input__tags-input"
22
- v-model="input"
23
- v-bind="{ ...$attrs, ...$props }"
24
- :tags="tags"
25
- :autocomplete-items="autocompleteOptions"
21
+ <vue-multiselect
22
+ ref="vue-multiselect"
23
+ :close-on-select="false"
24
+ :disabled="disabled"
25
+ :internal-search="!searchMethod"
26
+ :label="trackBy ? optionLabel : null"
27
+ :loading="false"
28
+ :options="selectOptions"
26
29
  :placeholder="placeholder || label"
27
- :separators="[' ']"
30
+ :taggable="taggable"
31
+ :track-by="trackBy"
32
+ :value="selectValue"
33
+ class="wt-tags-input__select"
34
+ multiple
35
+ v-bind="$attrs"
28
36
  v-on="listeners"
29
37
  >
30
- <template slot="tag-actions" slot-scope="{ index, performDelete }">
38
+
39
+ <!-- Slot that is used for all selected options (tags)-->
40
+ <template v-slot:tag="{ option, remove }">
41
+ <wt-chip class="multiselect__custom-tag">
42
+ {{ getTagOptionLabel({ option, optionLabel }) }}
43
+ <wt-icon-btn
44
+ icon="close--filled"
45
+ size="sm"
46
+ @click="remove(option)"
47
+ ></wt-icon-btn>
48
+ </wt-chip>
49
+ </template>
50
+
51
+ <!-- Slot for custom option template -->
52
+ <template v-slot:option="{ option }">
53
+ <slot name="option" v-bind="{ option, optionLabel }">
54
+ {{ getTagOptionLabel({ option, optionLabel }) }}
55
+ </slot>
56
+ </template>
57
+
58
+ <!-- Element for opening and closing the dropdown -->
59
+ <template v-slot:caret="{ toggle }">
60
+ <!-- @mousedown.native.prevent.stop="toggle": https://github.com/shentao/vue-multiselect/issues/1204#issuecomment-615114727 -->
31
61
  <wt-icon-btn
32
- icon="close--filled"
33
- size="sm"
62
+ :disabled="disabled"
63
+ class="multiselect__select"
34
64
  color="active"
35
- @click="performDelete(index)"
65
+ icon="arrow-down"
66
+ @mousedown.native.prevent.stop="toggle"
36
67
  ></wt-icon-btn>
37
68
  </template>
38
- </vue-tags-input>
69
+
70
+ <template v-slot:beforeList>
71
+ <div v-show="isLoading" class="multiselect__loading-wrapper">
72
+ <wt-loader size="sm"></wt-loader>
73
+ </div>
74
+ </template>
75
+
76
+ <template v-if="showIntersectionObserver" v-slot:afterList>
77
+ <div v-observe-visibility="handleAfterListIntersect"></div>
78
+ </template>
79
+ </vue-multiselect>
39
80
 
40
81
  <wt-input-info
41
82
  v-if="isValidation"
@@ -46,219 +87,71 @@
46
87
  </template>
47
88
 
48
89
  <script>
49
- import VueTagsInput from '@johmun/vue-tags-input';
50
- import validationMixin from '../../../mixins/validationMixin/validationMixin';
90
+ import multiselectMixin from '../wt-select/mixins/multiselectMixin';
51
91
 
52
92
  export default {
53
93
  name: 'wt-tags-input',
54
- mixins: [validationMixin],
55
- components: { VueTagsInput },
94
+ mixins: [multiselectMixin],
56
95
  props: {
57
96
  value: {
58
97
  type: Array,
59
98
  },
60
-
61
- autocompleteItems: {
62
- type: Array,
63
- default: () => [],
64
- },
65
-
66
- label: {
99
+ trackBy: {
67
100
  type: String,
68
- default: '',
101
+ default: null,
69
102
  },
70
-
71
- placeholder: {
72
- type: String,
73
- default: '',
74
- },
75
-
76
- disabled: {
103
+ taggable: {
77
104
  type: Boolean,
78
105
  default: false,
79
106
  },
80
-
81
- autocompleteMinLength: {
82
- type: Number,
83
- default: 0,
84
- },
85
-
86
- addOnlyFromAutocomplete: {
107
+ manualTagging: {
87
108
  type: Boolean,
88
109
  default: false,
89
- },
110
+ description: `
111
+ False: "tag" method automatically pushes { optionLabel | "name", trackBy } object to value
112
+ array and $emits "input" event.
90
113
 
91
- autocompleteFilterDuplicates: {
92
- type: Boolean,
93
- default: true,
94
- },
95
-
96
- labelProps: {
97
- type: Object,
98
- description: 'Object with props, passed down to wt-label as props',
99
- },
100
- },
101
-
102
- data: () => ({
103
- input: '',
104
- }),
105
-
106
- computed: {
107
- hasLabel() {
108
- return !!(this.label || this.$slots.label);
109
- },
110
-
111
- requiredLabel() {
112
- return this.required ? `${this.label}*` : this.label;
113
- },
114
-
115
- tags() {
116
- if (!this.value) return [];
117
- return this.value.map((item) => ({ text: item.name || item.value, ...item }));
118
- },
119
-
120
- autocompleteOptions() {
121
- return this.autocompleteItems
122
- .map((item) => ({
123
- text: typeof item === 'object' ? item.name || item.value : item,
124
- ...item,
125
- }))
126
- .filter((item) => item.text?.includes(this.input));
127
- },
128
-
129
- listeners() {
130
- return {
131
- ...this.$listeners,
132
- input: this.searchTags,
133
- 'tags-changed': this.changeTags,
134
- };
114
+ True: "tag" method only $emits "tag" event. Tag addition is responsibility of client side.
115
+ `,
135
116
  },
136
117
  },
137
118
 
138
119
  methods: {
139
- searchTags(search) {
140
- this.$emit('search', search);
120
+ tag(searchQuery, id) {
121
+ this.$emit('tag', searchQuery, id);
122
+ if (!this.manualTagging) {
123
+ const tag = this.trackBy ? {
124
+ [this.optionLabel || 'name']: searchQuery,
125
+ [this.trackBy]: id || searchQuery,
126
+ } : searchQuery;
127
+ const value = [...this.value, tag];
128
+ this.input(value);
129
+ }
141
130
  },
142
-
143
- changeTags(tags) {
144
- this.$emit('input', tags);
131
+ getTagOptionLabel({ optionLabel, option }) {
132
+ /*
133
+ Multiselect creates new tags with "label" property by default, so we need to handle
134
+ it as well
135
+ */
136
+ const label = this.getOptionLabel({ optionLabel, option });
137
+ return typeof label === 'object' ? option.label : label;
145
138
  },
146
139
  },
147
140
  };
148
141
  </script>
149
142
 
150
143
  <style lang="scss" scoped>
151
- @import '../../../css/components/atoms/wt-chip/wt-chip';
144
+ @import '../../../css/components/molecules/wt-select/multiselect';
152
145
 
153
146
  .wt-tags-input {
154
147
  width: 100%;
155
148
  }
156
149
 
157
- // increase specificity
158
- .wt-tags-input .wt-tags-input__tags-input {
159
- width: 100%;
160
- max-width: 100%; // reset default
161
-
162
- ::v-deep {
163
- .ti-input {
164
- box-sizing: border-box;
165
- min-height: var(--tags-input-min-height); // preserve height, if tags is empty
166
- padding: 0; // reset default
167
- border: var(--tags-input-border);
168
- border-color: var(--tags-input-border-color);
169
- border-radius: var(--border-radius);
170
- transition: var(--transition);
171
-
172
- .ti-tags {
173
- display: flex;
174
- align-items: center;
175
- gap: var(--tags-input-tags-gap);
176
- padding: var(--tags-input-padding);
177
- margin: 0; // reset default
178
- word-break: break-word; //break long word
179
- }
180
-
181
- .ti-new-tag-input-wrapper {
182
- padding: 0; // reset default
183
- margin: 0; // reset default
184
- }
185
-
186
- .ti-new-tag-input {
187
- @extend %typo-body-1;
188
- @include wt-placeholder;
189
-
190
- border-radius: var(--border-radius);
191
- }
192
-
193
- .ti-tag {
194
- @extend .wt-chip;
195
- display: flex;
196
- height: fit-content;
197
- margin: 0; // reset default
198
- transition: var(--transition);
199
-
200
- .ti-actions {
201
- margin-left: var(--tags-input-tag-actions-margin-left);
202
- }
203
- // pre-delete state after 1 backspace press
204
- &.ti-deletion-mark { // FIXME
205
- color: var(--tags-input-tag-text-color--deletion);
206
- background: var(--tags-input-tag-bg-color--deletion);
207
-
208
- .wt-icon__icon {
209
- fill: var(--tags-input-tag-text-color--deletion);
210
- }
211
- }
212
- }
213
- }
214
-
215
- .ti-autocomplete {
216
- @extend %wt-distant-scrollbar;
217
- max-height: var(--tags-input-autocomplete-max-height);
218
- border: var(--tags-input-autocomplete-border);
219
- border-color: var(--tags-input-autocomplete-border-color);
220
- border-radius: var(--border-radius);
221
- transition: var(--transition);
222
- overflow: auto;
223
-
224
- .ti-item {
225
- background: transparent; // reset default
226
-
227
- & > div {
228
- @extend %typo-body-1;
229
- padding: var(--tags-input-autocomplete-option-padding);
230
- color: var(--tags-input-autocomplete-option--text-color);
231
- transition: var(--transition);
232
-
233
- &:hover {
234
- color: var(--tags-input-option-text-color--hover);
235
- background: var(--tags-input-option-bg-color--hover);
236
- }
237
- }
238
- }
239
- }
240
- }
241
- }
242
-
243
150
  .wt-tags-input:hover,
244
151
  .wt-tags-input:focus-within {
245
152
  .wt-label {
246
153
  color: var(--form-label--hover-color);
247
154
  }
248
-
249
- .wt-tags-input__tags-input ::v-deep {
250
- .ti-new-tag-input {
251
- border-color: var(--form-border--hover-color);
252
- }
253
- }
254
- }
255
-
256
- .wt-tags-input:focus-within {
257
- .wt-tags-input__tags-input ::v-deep {
258
- .ti-new-tag-input {
259
- @include wt-placeholder('focus');
260
- }
261
- }
262
155
  }
263
156
 
264
157
  .wt-tags-input--invalid,
@@ -267,36 +160,37 @@ export default {
267
160
  .wt-label {
268
161
  color: var(--false-color);
269
162
  }
270
-
271
- .wt-tags-input__tags-input {
272
- ::v-deep {
273
- .ti-input {
274
- border-color: var(--false-color);
275
- }
276
- }
277
- }
278
163
  }
279
164
 
280
165
  .wt-tags-input--disabled {
281
166
  .wt-tags-input__tags-input {
282
167
  width: 100%;
283
168
  max-width: 100%; // reset default
169
+ }
170
+ }
284
171
 
285
- ::v-deep {
286
- .ti-input {
287
- border-color: transparent;
172
+ .wt-tags-input .multiselect ::v-deep {
173
+ .multiselect__tags {
174
+ padding-bottom: 0;
288
175
 
289
- .ti-new-tag-input-wrapper {
290
- display: none;
291
- }
176
+ .multiselect__tags-wrap {
177
+ display: flex;
178
+ flex-wrap: wrap;
179
+ }
292
180
 
293
- .ti-tag {
294
- .ti-actions {
295
- display: none;
296
- }
297
- }
298
- }
181
+ .multiselect__custom-tag {
182
+ display: flex;
183
+ align-items: center;
184
+ flex-wrap: nowrap;
185
+ max-width: 100%;
186
+ margin: 0 var(--spacing-xs) calc(var(--spacing-xs) - 1px) 0; // border bottom
187
+ word-break: break-all;
188
+ gap: var(--spacing-xs);
299
189
  }
300
190
  }
191
+
192
+ .multiselect__input {
193
+ margin-bottom: calc(var(--spacing-xs) - 1px); // border
194
+ }
301
195
  }
302
196
  </style>
@@ -1,6 +1,6 @@
1
1
 
2
2
  :root {
3
- --chip-padding: var(--spacing-3xs) var(--spacing-sm);
3
+ --chip-padding: var(--spacing-2xs) var(--spacing-sm);
4
4
  --chip-border-radius: var(--spacing-lg);
5
5
  --chip-bg-color: var(--accent-secondary-color);
6
6
  --chip-bg-color--outline: var(--secondary-color);
@@ -0,0 +1,193 @@
1
+ @import '~vue-multiselect/dist/vue-multiselect.min.css';
2
+
3
+ .multiselect ::v-deep {
4
+ cursor: pointer;
5
+ position: relative;
6
+
7
+ .multiselect__select {
8
+ position: absolute;
9
+ top: var(--select-icon-top-pos);
10
+ right: var(--select-caret-right-pos);
11
+ padding: 0; // reset default
12
+ transition: var(--transition);
13
+ width: fit-content; // reset default
14
+ height: fit-content; // reset default
15
+
16
+ &:before {
17
+ display: none; // reset default
18
+ }
19
+ }
20
+
21
+ .multiselect__limit {
22
+ position: absolute;
23
+ top: var(--select-chip-top-pos);
24
+ }
25
+
26
+ .multiselect__clear {
27
+ position: absolute;
28
+ z-index: 1;
29
+ top: var(--select-icon-top-pos);
30
+ }
31
+
32
+
33
+ .multiselect__tags {
34
+ color: var(--form-input-color);
35
+ border: var(--select-tags-border);
36
+ border-color: var(--form-border-color);
37
+ border-radius: var(--border-radius);
38
+ }
39
+
40
+
41
+ .multiselect__input,
42
+ .multiselect__single,
43
+ .multiselect__placeholder {
44
+ @extend %typo-body-1;
45
+ @include wt-placeholder;
46
+ background: transparent;
47
+ padding: 0; // reset default
48
+ margin: 0; // reset default
49
+ }
50
+
51
+ .multiselect__input:focus {
52
+ @include wt-placeholder('focus');
53
+ }
54
+
55
+ .multiselect__custom-tag,
56
+ .multiselect__single-label {
57
+ color: var(--form-input-color);
58
+ }
59
+
60
+ .multiselect__placeholder {
61
+ @extend %wt-placeholder;
62
+ overflow: hidden;
63
+ max-width: 100%;
64
+
65
+ // text overflow 3 dots
66
+ white-space: nowrap;
67
+ text-overflow: ellipsis;
68
+ }
69
+
70
+ .multiselect__content-wrapper {
71
+ @extend %wt-distant-scrollbar;
72
+ transition: var(--transition);
73
+ border: var(--select-tags-border);
74
+ border-color: var(--form-border-color);
75
+ border-radius: var(--border-radius);
76
+ }
77
+
78
+ .multiselect__option {
79
+ @extend %typo-body-1;
80
+ padding: var(--select-options-padding);
81
+
82
+ &:after {
83
+ display: none;
84
+ }
85
+
86
+
87
+ &--highlight {
88
+ color: var(--form-input-color);
89
+ //background: var(--select-option--hover-color);
90
+ background: none;
91
+ }
92
+
93
+
94
+ &--selected {
95
+ font-weight: normal;
96
+ background: var(--select-option--active-color)
97
+ }
98
+ }
99
+ }
100
+
101
+ .multiselect--active ::v-deep {
102
+ .multiselect__select .wt-icon__icon {
103
+ fill: var(--icon-color--hover);
104
+ }
105
+
106
+ .multiselect__tags {
107
+ border-color: var(--form-border--active-color);
108
+
109
+ .multiselect__placeholder {
110
+ @extend %wt-placeholder--focus;
111
+ }
112
+ }
113
+
114
+ }
115
+
116
+ .wt-select:hover,
117
+ .wt-tags-input:hover {
118
+ .multiselect ::v-deep {
119
+ .multiselect__tags {
120
+ border-color: var(--form-border--hover-color);
121
+ }
122
+
123
+ .multiselect__select,
124
+ .multiselect__clear {
125
+ .wt-icon__icon {
126
+ fill: var(--icon-color--hover);
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ .wt-select--loading ::v-deep {
133
+ .multiselect__content-wrapper {
134
+ overflow-y: hidden;
135
+ }
136
+
137
+ .multiselect__loading-wrapper {
138
+ position: sticky;
139
+ z-index: 1;
140
+ top: 0;
141
+ left: 0;
142
+ display: flex;
143
+ align-items: center;
144
+ justify-content: center;
145
+ width: 100%;
146
+ height: 300px; // max dropdown panel height
147
+ backdrop-filter: blur(4px);
148
+ }
149
+ }
150
+
151
+
152
+ .wt-select.wt-select--invalid,
153
+ .wt-tags-input.wt-tags-input--invalid {
154
+ .multiselect ::v-deep {
155
+ .multiselect__tags {
156
+ border-color: var(--false-color);
157
+ outline: none; // prevent outline overlapping false color
158
+ }
159
+ }
160
+ }
161
+
162
+ .wt-select--disabled,
163
+ .wt-tags-input--disabled {
164
+ pointer-events: none;
165
+
166
+ .multiselect--disabled {
167
+ opacity: 1;
168
+ background: none;
169
+ }
170
+
171
+ .multiselect__select {
172
+ background: transparent; // reset default
173
+ }
174
+
175
+ .multiselect__arrow-caret ::v-deep .wt-icon__icon,
176
+ .multiselect__clear ::v-deep .wt-icon__icon {
177
+ fill: var(--icon-color-disabled);
178
+ }
179
+
180
+ .multiselect {
181
+ ::v-deep {
182
+ .multiselect__tags {
183
+ border-color: var(--form-border--disabled-color);
184
+ background: var(--form-border--disabled-color);
185
+
186
+ .multiselect__placeholder {
187
+ @extend %wt-placeholder--disabled;
188
+ //color: var(--form-placeholder--disabled-color);
189
+ }
190
+ }
191
+ }
192
+ }
193
+ }
@@ -1,59 +1,12 @@
1
1
 
2
2
  :root {
3
- --select-caret-top-pos: var(--spacing-xs);
4
- --select-caret-right-pos: var(--spacing-xs);
5
- --select-caret-z-index: 1;
3
+ --select-icon-top-pos: var(--spacing-xs);
4
+ --select-chip-top-pos: var(--spacing-xs);
6
5
 
7
- --select-clear-top-pos: var(--spacing-xs);
8
-
9
- --select-clear-right-pos: calc(
10
- var(--input-padding)
11
- + var(--icon-md-size)
12
- + var(--select-caret-right-pos)
13
- ); // spacing + caret icon + caret right pos
14
-
15
- --select-clear-z-index: var(--select-caret-z-index);
16
-
17
- --select-tags-min-height: calc(24px + var(--spacing-xs) * 2); // 24px %typo-body-1 line-height
18
-
19
- --select-tags-padding:
20
- var(--input-padding)
21
- calc(
22
- var(--input-padding)
23
- + var(--icon-md-size)
24
- + var(--select-caret-right-pos)
25
- )
26
- var(--input-padding)
27
- var(--input-padding); // right pos = 10px spacing + caret icon size + caret offset
28
-
29
- --select-tags-padding--clearable:
30
- var(--input-padding)
31
- calc(
32
- var(--input-padding)
33
- + var(--icon-md-size)
34
- + var(--select-clear-right-pos)
35
- )
36
- var(--input-padding)
37
- var(--input-padding);
6
+ --select-caret-right-pos: var(--input-padding);
38
7
 
39
8
  --select-tags-border: var(--input-border); // right pos = 10px spacing + clear icon size + clear offset
40
9
 
41
- --select-input-min-height: 18px;
42
-
43
- --select-chip-right-pos: calc(
44
- var(--spacing-xs)
45
- + var(--icon-md-size)
46
- + var(--spacing-xs)
47
- ); // 2 spacings + arrow down icon
48
-
49
- --select-chip-right-pos--clearable: calc(
50
- var(--spacing-xs)
51
- + var(--icon-md-size)
52
- + var(--select-chip-right-pos)
53
- ); // spacing after bottom arrow + "reset" sm icon
54
-
55
- --select-chip-z-index: 1;
56
-
57
10
  --select-options-padding: var(--input-padding);
58
11
 
59
12
  --select-option--hover-color: var(--accent-secondary-color);
@@ -92,6 +92,14 @@ export default {
92
92
  [snakeToCamel(AgentStatus.BreakOut)]: 'Break out',
93
93
  },
94
94
  },
95
+ flow: {
96
+ type: {
97
+ [EngineRoutingSchemaType.Chat]: 'Chat',
98
+ [EngineRoutingSchemaType.Voice]: 'Voice',
99
+ [EngineRoutingSchemaType.Service]: 'Service',
100
+ [EngineRoutingSchemaType.Processing]: 'Processing',
101
+ },
102
+ },
95
103
  },
96
104
  channel: {
97
105
  state: {
@@ -90,6 +90,14 @@ export default {
90
90
  [snakeToCamel(AgentStatus.BreakOut)]: 'Принудительный перерыв',
91
91
  },
92
92
  },
93
+ flow: {
94
+ type: {
95
+ [EngineRoutingSchemaType.Chat]: 'Чат',
96
+ [EngineRoutingSchemaType.Voice]: 'Голос',
97
+ [EngineRoutingSchemaType.Service]: 'Сервис',
98
+ [EngineRoutingSchemaType.Processing]: 'Обработка',
99
+ },
100
+ },
93
101
  },
94
102
  channel: {
95
103
  state: {