@webitel/ui-sdk 2.0.32-2 → 2.1.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.
@@ -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,74 @@
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
+
123
+ const tag = this.trackBy ? {
124
+ [this.optionLabel || 'name']: searchQuery,
125
+ [this.trackBy]: id || searchQuery,
126
+ } : searchQuery;
127
+
128
+ this.options.unshift(tag);
129
+ if (!this.manualTagging) {
130
+ const value = [...this.value, tag];
131
+ this.input(value);
132
+ }
141
133
  },
142
-
143
- changeTags(tags) {
144
- this.$emit('input', tags);
134
+ getTagOptionLabel({ optionLabel, option }) {
135
+ /*
136
+ Multiselect creates new tags with "label" property by default, so we need to handle
137
+ it as well
138
+ */
139
+ const label = this.getOptionLabel({ optionLabel, option });
140
+ return typeof label === 'object' ? option.label : label;
145
141
  },
146
142
  },
147
143
  };
148
144
  </script>
149
145
 
150
146
  <style lang="scss" scoped>
151
- @import '../../../css/components/atoms/wt-chip/wt-chip';
147
+ @import '../../../css/components/molecules/wt-select/multiselect';
152
148
 
153
149
  .wt-tags-input {
154
150
  width: 100%;
155
151
  }
156
152
 
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
153
  .wt-tags-input:hover,
244
154
  .wt-tags-input:focus-within {
245
155
  .wt-label {
246
156
  color: var(--form-label--hover-color);
247
157
  }
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
158
  }
263
159
 
264
160
  .wt-tags-input--invalid,
@@ -267,36 +163,37 @@ export default {
267
163
  .wt-label {
268
164
  color: var(--false-color);
269
165
  }
270
-
271
- .wt-tags-input__tags-input {
272
- ::v-deep {
273
- .ti-input {
274
- border-color: var(--false-color);
275
- }
276
- }
277
- }
278
166
  }
279
167
 
280
168
  .wt-tags-input--disabled {
281
169
  .wt-tags-input__tags-input {
282
170
  width: 100%;
283
171
  max-width: 100%; // reset default
172
+ }
173
+ }
284
174
 
285
- ::v-deep {
286
- .ti-input {
287
- border-color: transparent;
175
+ .wt-tags-input .multiselect ::v-deep {
176
+ .multiselect__tags {
177
+ padding-bottom: 0;
288
178
 
289
- .ti-new-tag-input-wrapper {
290
- display: none;
291
- }
179
+ .multiselect__tags-wrap {
180
+ display: flex;
181
+ flex-wrap: wrap;
182
+ }
292
183
 
293
- .ti-tag {
294
- .ti-actions {
295
- display: none;
296
- }
297
- }
298
- }
184
+ .multiselect__custom-tag {
185
+ display: flex;
186
+ align-items: center;
187
+ flex-wrap: nowrap;
188
+ max-width: 100%;
189
+ margin: 0 var(--spacing-xs) calc(var(--spacing-xs) - 1px) 0; // border bottom
190
+ word-break: break-all;
191
+ gap: var(--spacing-xs);
299
192
  }
300
193
  }
194
+
195
+ .multiselect__input {
196
+ margin-bottom: calc(var(--spacing-xs) - 1px); // border
197
+ }
301
198
  }
302
199
  </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,194 @@
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
+ padding: calc(var(--spacing-xs) - 1px); // borders
35
+ color: var(--form-input-color);
36
+ border: var(--select-tags-border);
37
+ border-color: var(--form-border-color);
38
+ border-radius: var(--border-radius);
39
+ }
40
+
41
+
42
+ .multiselect__input,
43
+ .multiselect__single,
44
+ .multiselect__placeholder {
45
+ @extend %typo-body-1;
46
+ @include wt-placeholder;
47
+ background: transparent;
48
+ padding: 0; // reset default
49
+ margin: 0; // reset default
50
+ }
51
+
52
+ .multiselect__input:focus {
53
+ @include wt-placeholder('focus');
54
+ }
55
+
56
+ .multiselect__custom-tag,
57
+ .multiselect__single-label {
58
+ color: var(--form-input-color);
59
+ }
60
+
61
+ .multiselect__placeholder {
62
+ @extend %wt-placeholder;
63
+ overflow: hidden;
64
+ max-width: 100%;
65
+
66
+ // text overflow 3 dots
67
+ white-space: nowrap;
68
+ text-overflow: ellipsis;
69
+ }
70
+
71
+ .multiselect__content-wrapper {
72
+ @extend %wt-distant-scrollbar;
73
+ transition: var(--transition);
74
+ border: var(--select-tags-border);
75
+ border-color: var(--form-border-color);
76
+ border-radius: var(--border-radius);
77
+ }
78
+
79
+ .multiselect__option {
80
+ @extend %typo-body-1;
81
+ padding: var(--select-options-padding);
82
+
83
+ &:after {
84
+ display: none;
85
+ }
86
+
87
+
88
+ &--highlight {
89
+ color: var(--form-input-color);
90
+ //background: var(--select-option--hover-color);
91
+ background: none;
92
+ }
93
+
94
+
95
+ &--selected {
96
+ font-weight: normal;
97
+ background: var(--select-option--active-color)
98
+ }
99
+ }
100
+ }
101
+
102
+ .multiselect--active ::v-deep {
103
+ .multiselect__select .wt-icon__icon {
104
+ fill: var(--icon-color--hover);
105
+ }
106
+
107
+ .multiselect__tags {
108
+ border-color: var(--form-border--active-color);
109
+
110
+ .multiselect__placeholder {
111
+ @extend %wt-placeholder--focus;
112
+ }
113
+ }
114
+
115
+ }
116
+
117
+ .wt-select:hover,
118
+ .wt-tags-input:hover {
119
+ .multiselect ::v-deep {
120
+ .multiselect__tags {
121
+ border-color: var(--form-border--hover-color);
122
+ }
123
+
124
+ .multiselect__select,
125
+ .multiselect__clear {
126
+ .wt-icon__icon {
127
+ fill: var(--icon-color--hover);
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ .wt-select--loading ::v-deep {
134
+ .multiselect__content-wrapper {
135
+ overflow-y: hidden;
136
+ }
137
+
138
+ .multiselect__loading-wrapper {
139
+ position: sticky;
140
+ z-index: 1;
141
+ top: 0;
142
+ left: 0;
143
+ display: flex;
144
+ align-items: center;
145
+ justify-content: center;
146
+ width: 100%;
147
+ height: 300px; // max dropdown panel height
148
+ backdrop-filter: blur(4px);
149
+ }
150
+ }
151
+
152
+
153
+ .wt-select.wt-select--invalid,
154
+ .wt-tags-input.wt-tags-input--invalid {
155
+ .multiselect ::v-deep {
156
+ .multiselect__tags {
157
+ border-color: var(--false-color);
158
+ outline: none; // prevent outline overlapping false color
159
+ }
160
+ }
161
+ }
162
+
163
+ .wt-select--disabled,
164
+ .wt-tags-input--disabled {
165
+ pointer-events: none;
166
+
167
+ .multiselect--disabled {
168
+ opacity: 1;
169
+ background: none;
170
+ }
171
+
172
+ .multiselect__select {
173
+ background: transparent; // reset default
174
+ }
175
+
176
+ .multiselect__arrow-caret ::v-deep .wt-icon__icon,
177
+ .multiselect__clear ::v-deep .wt-icon__icon {
178
+ fill: var(--icon-color-disabled);
179
+ }
180
+
181
+ .multiselect {
182
+ ::v-deep {
183
+ .multiselect__tags {
184
+ border-color: var(--form-border--disabled-color);
185
+ background: var(--form-border--disabled-color);
186
+
187
+ .multiselect__placeholder {
188
+ @extend %wt-placeholder--disabled;
189
+ //color: var(--form-placeholder--disabled-color);
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
@@ -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);
@@ -98,7 +98,6 @@ export default {
98
98
  [EngineRoutingSchemaType.Voice]: 'Voice',
99
99
  [EngineRoutingSchemaType.Service]: 'Service',
100
100
  [EngineRoutingSchemaType.Processing]: 'Processing',
101
- [EngineRoutingSchemaType.Default]: 'Default',
102
101
  },
103
102
  },
104
103
  },
@@ -96,7 +96,6 @@ export default {
96
96
  [EngineRoutingSchemaType.Voice]: 'Голос',
97
97
  [EngineRoutingSchemaType.Service]: 'Сервис',
98
98
  [EngineRoutingSchemaType.Processing]: 'Обработка',
99
- [EngineRoutingSchemaType.Default]: 'По умолчанию',
100
99
  },
101
100
  },
102
101
  },
@@ -96,7 +96,6 @@ export default {
96
96
  [EngineRoutingSchemaType.Voice]: 'Голос',
97
97
  [EngineRoutingSchemaType.Service]: 'Сервіс',
98
98
  [EngineRoutingSchemaType.Processing]: 'Обробка',
99
- [EngineRoutingSchemaType.Default]: 'За замовчуванням',
100
99
  },
101
100
  },
102
101
  },