mediacube-ui 0.1.424 → 0.1.426

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,1261 +1,1261 @@
1
- <!--<template>-->
2
- <!-- <div :ref="field_key" :dir="dir" :class="classes" :style="styles">-->
3
- <!-- <div :for="name" class="mc-field-select__header">-->
4
- <!-- &lt;!&ndash; @slot Слот заголовка &ndash;&gt;-->
5
- <!-- <slot name="header">-->
6
- <!-- <mc-title v-if="hasTitle" :ellipsis="false" max-width="100%" weight="medium">-->
7
- <!-- {{ computedTitle }}-->
8
- <!-- </mc-title>-->
9
- <!-- </slot>-->
10
- <!-- </div>-->
11
- <!-- <div class="mc-field-select__main">-->
12
- <!-- <multi-select-->
13
- <!-- :ref="key"-->
14
- <!-- v-bind="tagBind"-->
15
- <!-- @input="handleChange"-->
16
- <!-- @tag="handleTag"-->
17
- <!-- @search-change="handleSearchChange"-->
18
- <!-- @open="handleOpen"-->
19
- <!-- @close="handleClose"-->
20
- <!-- >-->
21
- <!-- <template v-if="isShowLimitToggle" slot="caret">-->
22
- <!-- <div :class="computedCaretClass" @click="toggleOptions" />-->
23
- <!-- </template>-->
24
- <!-- <template v-if="collapsed && !is_show_all_options" slot="limit">-->
25
- <!-- <mc-title variation="body" class="mc-field-select__limit-text">-->
26
- <!-- {{ limitText }}-->
27
- <!-- </mc-title>-->
28
- <!-- </template>-->
29
- <!-- <template slot="singleLabel" slot-scope="{ option }">-->
30
- <!-- <mc-preview v-if="optionWithPreview" class="option__desc" size="l">-->
31
- <!-- <template v-if="option?.icon || option?.image" #left>-->
32
- <!-- <mc-avatar v-if="option.image" :src="option.image" size="400" />-->
33
- <!-- <mc-svg-icon v-else :name="option.icon" :color="option.iconColor || 'main'" size="400" />-->
34
- <!-- </template>-->
35
- <!-- <mc-title slot="top" weight="semi-bold" v-html="option.name" />-->
36
- <!-- &lt;!&ndash; Слот для замены стандартной стрелки при выведенном превью &ndash;&gt;-->
37
- <!-- <slot slot="right" name="arrow" />-->
38
- <!-- <mc-title slot="bottom" color="gray">-->
39
- <!-- {{ option.text }}-->
40
- <!-- </mc-title>-->
41
- <!-- </mc-preview>-->
42
- <!-- <div v-else class="mc-field-select__single-label">-->
43
- <!-- <div v-if="hasPrepend" class="mc-field-select__prepend">-->
44
- <!-- <mc-avatar v-if="avatar" :src="avatar" />-->
45
- <!-- <mc-svg-icon v-else :name="icon" />-->
46
- <!-- </div>-->
47
- <!-- <div-->
48
- <!-- class="mc-field-select__label-text"-->
49
- <!-- :class="hasPrepend ? 'mc-field-select__label-text&#45;&#45;indent-left' : ''"-->
50
- <!-- >-->
51
- <!-- {{ option ? option.name : placeholder }}-->
52
- <!-- </div>-->
53
- <!-- </div>-->
54
- <!-- </template>-->
55
- <!-- <template slot="tag" slot-scope="{ option, remove }">-->
56
- <!-- <mc-chip-->
57
- <!-- variation="main-alpha-10-invert"-->
58
- <!-- class="multiselect__tag"-->
59
- <!-- text-color="black"-->
60
- <!-- size="s"-->
61
- <!-- :closable="!option.hasOwnProperty('is_closable') || option.is_closable"-->
62
- <!-- @click="remove(option)"-->
63
- <!-- >-->
64
- <!-- {{ option.name }}-->
65
- <!-- </mc-chip>-->
66
- <!-- </template>-->
67
- <!-- <template-->
68
- <!-- v-if="optionsTooltip || optionWithPreview || optionWithPreviewOnly"-->
69
- <!-- slot="option"-->
70
- <!-- slot-scope="{ option }"-->
71
- <!-- >-->
72
- <!-- <mc-title-->
73
- <!-- v-if="(optionWithPreview || optionWithPreviewOnly) && option.$isLabel"-->
74
- <!-- weight="semi-bold"-->
75
- <!-- variation="subtitle"-->
76
- <!-- >-->
77
- <!-- {{ option.$groupLabel }}-->
78
- <!-- </mc-title>-->
79
- <!-- <mc-preview v-else-if="optionWithPreview || optionWithPreviewOnly" class="option__desc" size="l">-->
80
- <!-- <template v-if="option?.icon || option?.image" #left>-->
81
- <!-- <mc-avatar v-if="option.image" :src="option.image" size="400" />-->
82
- <!-- <mc-svg-icon v-else :name="option.icon" :color="option.iconColor || 'main'" size="400" />-->
83
- <!-- </template>-->
84
- <!-- <mc-title slot="top" :weight="optionWithPreviewOnly ? '' : 'semi-bold'" v-html="option.name" />-->
85
- <!-- <mc-title v-if="!!option.text" slot="bottom" color="dark-gray" :pre-line="option.preLine"-->
86
- <!-- >{{ option.text }}-->
87
- <!-- </mc-title>-->
88
- <!-- </mc-preview>-->
89
- <!-- <mc-tooltip-->
90
- <!-- v-else-->
91
- <!-- class="mc-field-select__options-tooltip-target"-->
92
- <!-- max-width="m"-->
93
- <!-- color="black"-->
94
- <!-- placement="top"-->
95
- <!-- :content="option.name"-->
96
- <!-- >-->
97
- <!-- <span>{{ option.name }}</span>-->
98
- <!-- </mc-tooltip>-->
99
- <!-- </template>-->
100
- <!-- &lt;!&ndash; @slot Слот для текста, если ничего не найдено &ndash;&gt;-->
101
- <!-- <slot slot="noResult" name="noResult">-->
102
- <!-- <span>{{ noResultsText }}</span>-->
103
- <!-- </slot>-->
104
- <!-- </multi-select>-->
105
- <!-- </div>-->
106
- <!-- <div v-if="errorText || helpText || $slots.footer" class="mc-field-select__footer">-->
107
- <!-- <mc-title-->
108
- <!-- v-if="errorText"-->
109
- <!-- tag-name="div"-->
110
- <!-- color="red"-->
111
- <!-- variation="overline"-->
112
- <!-- max-width="100%"-->
113
- <!-- :ellipsis="false"-->
114
- <!-- >-->
115
- <!-- {{ errorText }}-->
116
- <!-- </mc-title>-->
117
- <!-- <br v-if="errorText" />-->
118
- <!-- &lt;!&ndash; @slot Слот доп. текста под инпутом &ndash;&gt;-->
119
- <!-- <slot name="footer">-->
120
- <!-- <mc-title-->
121
- <!-- v-if="helpText"-->
122
- <!-- tag-name="div"-->
123
- <!-- variation="overline"-->
124
- <!-- color="gray"-->
125
- <!-- max-width="100%"-->
126
- <!-- :ellipsis="false"-->
127
- <!-- >-->
128
- <!-- {{ helpText }}-->
129
- <!-- </mc-title>-->
130
- <!-- </slot>-->
131
- <!-- </div>-->
132
- <!-- </div>-->
133
- <!--</template>-->
134
-
135
- <!--<script>-->
136
- <!--import MultiSelect from 'vue-multiselect'-->
137
- <!--import McTitle from '../../McTitle/McTitle'-->
138
- <!--import McChip from '../../McChip/McChip'-->
139
- <!--import McTooltip from '../../McTooltip/McTooltip'-->
140
- <!--import McAvatar from '../../McAvatar/McAvatar'-->
141
- <!--import McSvgIcon from '../../McSvgIcon/McSvgIcon'-->
142
- <!--import McPreview from '../../../patterns/McPreview/McPreview'-->
143
- <!--import fieldErrors from '../../../mixins/fieldErrors'-->
144
- <!--import equalFieldHeight from '../../../mixins/equalFieldHeight'-->
145
- <!--import { LANGUAGES } from '../../../helpers/consts'-->
146
-
147
- <!--export default {-->
148
- <!-- name: 'McFieldSelect',-->
149
- <!-- components: { McSvgIcon, McAvatar, McTitle, McTooltip, MultiSelect, McPreview, McChip },-->
150
- <!-- mixins: [fieldErrors, equalFieldHeight],-->
151
- <!-- props: {-->
152
- <!-- /**-->
153
- <!-- * Заголовок поля:-->
154
- <!-- *-->
155
- <!-- */-->
156
- <!-- title: {-->
157
- <!-- type: String,-->
158
- <!-- default: null,-->
159
- <!-- },-->
160
-
161
- <!-- /**-->
162
- <!-- * Вспомогательный текст под инпутом:-->
163
- <!-- *-->
164
- <!-- */-->
165
- <!-- helpText: {-->
166
- <!-- type: String,-->
167
- <!-- default: null,-->
168
- <!-- },-->
169
- <!-- /**-->
170
- <!-- * Массив элементов-->
171
- <!-- * выпадающего списка-->
172
- <!-- * [-->
173
- <!-- * {-->
174
- <!-- * name: String,-->
175
- <!-- * value: String | Number,-->
176
- <!-- * text: String - доступен, если optionWithPreview=true-->
177
- <!-- * icon: String - доступен, если optionWithPreview=true-->
178
- <!-- * }-->
179
- <!-- * ]-->
180
- <!-- */-->
181
- <!-- options: {-->
182
- <!-- type: Array,-->
183
- <!-- required: true,-->
184
- <!-- },-->
185
- <!-- /**-->
186
- <!-- * Выполняется ли поиск из списка-->
187
- <!-- * при вводе в инпут-->
188
- <!-- */-->
189
- <!-- searchable: {-->
190
- <!-- type: Boolean,-->
191
- <!-- default: true,-->
192
- <!-- },-->
193
- <!-- /**-->
194
- <!-- * Множественный выбор-->
195
- <!-- */-->
196
- <!-- multiple: {-->
197
- <!-- type: Boolean,-->
198
- <!-- default: false,-->
199
- <!-- },-->
200
- <!-- /**-->
201
- <!-- * Скрывать из списка-->
202
- <!-- * выбранные элементы-->
203
- <!-- */-->
204
- <!-- hideSelected: {-->
205
- <!-- type: Boolean,-->
206
- <!-- default: true,-->
207
- <!-- },-->
208
- <!-- /**-->
209
- <!-- * Допустимо ли-->
210
- <!-- * пустое значение-->
211
- <!-- */-->
212
- <!-- allowEmpty: {-->
213
- <!-- type: Boolean,-->
214
- <!-- default: false,-->
215
- <!-- },-->
216
- <!-- /**-->
217
- <!-- * Отключенное состояние-->
218
- <!-- */-->
219
- <!-- disabled: {-->
220
- <!-- type: Boolean,-->
221
- <!-- default: false,-->
222
- <!-- },-->
223
- <!-- /**-->
224
- <!-- * Ссылка на аватар/картинку-->
225
- <!-- * в начале label-->
226
- <!-- */-->
227
- <!-- avatar: {-->
228
- <!-- type: String,-->
229
- <!-- default: null,-->
230
- <!-- },-->
231
- <!-- /**-->
232
- <!-- * Имя иконки-->
233
- <!-- * в начале label-->
234
- <!-- */-->
235
- <!-- icon: {-->
236
- <!-- type: String,-->
237
- <!-- default: null,-->
238
- <!-- },-->
239
- <!-- /**-->
240
- <!-- * Цвет фона-->
241
- <!-- */-->
242
- <!-- backgroundColor: {-->
243
- <!-- type: String,-->
244
- <!-- default: null,-->
245
- <!-- },-->
246
- <!-- /**-->
247
- <!-- * placeholder-->
248
- <!-- */-->
249
- <!-- placeholder: {-->
250
- <!-- type: String,-->
251
- <!-- default: '',-->
252
- <!-- },-->
253
- <!-- /**-->
254
- <!-- * Направление открытия списка:-->
255
- <!-- * `above (top), below (bottom), auto`-->
256
- <!-- */-->
257
- <!-- openDirection: {-->
258
- <!-- type: String,-->
259
- <!-- default: 'auto',-->
260
- <!-- },-->
261
-
262
- <!-- taggable: {-->
263
- <!-- type: Boolean,-->
264
- <!-- default: false,-->
265
- <!-- },-->
266
- <!-- /**-->
267
- <!-- * Помечать в списке выбранные-->
268
- <!-- * элементы-->
269
- <!-- */-->
270
- <!-- showLabels: {-->
271
- <!-- type: Boolean,-->
272
- <!-- default: false,-->
273
- <!-- },-->
274
-
275
- <!-- internalSearch: {-->
276
- <!-- type: Boolean,-->
277
- <!-- default: true,-->
278
- <!-- },-->
279
- <!-- /**-->
280
- <!-- * Значение-->
281
- <!-- */-->
282
- <!-- value: {-->
283
- <!-- default: null,-->
284
- <!-- },-->
285
- <!-- /**-->
286
- <!-- * Ошибки-->
287
- <!-- */-->
288
- <!-- errors: {-->
289
- <!-- type: Array,-->
290
- <!-- default: null,-->
291
- <!-- },-->
292
- <!-- /**-->
293
- <!-- * Name-->
294
- <!-- */-->
295
- <!-- name: {-->
296
- <!-- type: String,-->
297
- <!-- required: true,-->
298
- <!-- },-->
299
- <!-- /**-->
300
- <!-- * Если нужен тултип-->
301
- <!-- * над элементами списка-->
302
- <!-- */-->
303
- <!-- optionsTooltip: {-->
304
- <!-- type: Boolean,-->
305
- <!-- default: false,-->
306
- <!-- },-->
307
- <!-- /**-->
308
- <!-- * Если режим taggable && searchValueInOptions то добавлять введенный тег в опции-->
309
- <!-- */-->
310
- <!-- searchValueInOptions: {-->
311
- <!-- type: Boolean,-->
312
- <!-- default: true,-->
313
- <!-- },-->
314
- <!-- /**-->
315
- <!-- * Группировка { label: 'label text', values: Array of objects }-->
316
- <!-- */-->
317
- <!-- groupKeys: {-->
318
- <!-- type: Object,-->
319
- <!-- default: null,-->
320
- <!-- },-->
321
- <!-- required: {-->
322
- <!-- type: Boolean,-->
323
- <!-- default: false,-->
324
- <!-- },-->
325
- <!-- /**-->
326
- <!-- * Если айтемам в селекте нужны превью с иконками и описанием-->
327
- <!-- */-->
328
- <!-- optionWithPreview: {-->
329
- <!-- type: Boolean,-->
330
- <!-- default: false,-->
331
- <!-- },-->
332
- <!-- /**-->
333
- <!-- * Если айтемам в селекте нужны превью с иконками и описанием-->
334
- <!-- * (только в выпадающем списке. Выбранное значение будет выводиться без описания)-->
335
- <!-- */-->
336
- <!-- optionWithPreviewOnly: {-->
337
- <!-- type: Boolean,-->
338
- <!-- default: false,-->
339
- <!-- },-->
340
- <!-- tabindex: {-->
341
- <!-- type: [String, Number],-->
342
- <!-- default: null,-->
343
- <!-- },-->
344
- <!-- /**-->
345
- <!-- * Если нужно ограничить максимальную высоту блока с выбранными элементами-->
346
- <!-- */-->
347
- <!-- maxHeight: {-->
348
- <!-- type: String,-->
349
- <!-- default: null,-->
350
- <!-- },-->
351
- <!-- /**-->
352
- <!-- * Рендерить ли выпадающий список абсолютно, что бы помещался в ограниченном пространстве-->
353
- <!-- * */-->
354
- <!-- renderAbsoluteList: {-->
355
- <!-- type: Boolean,-->
356
- <!-- default: false,-->
357
- <!-- },-->
358
- <!-- /**-->
359
- <!-- * Для какого языка селект-->
360
- <!-- */-->
361
- <!-- locale: {-->
362
- <!-- type: String,-->
363
- <!-- default: null,-->
364
- <!-- },-->
365
- <!-- /**-->
366
- <!-- * Текст для пустого селекта, когда неичего не найдено-->
367
- <!-- */-->
368
- <!-- noResultsText: {-->
369
- <!-- type: String,-->
370
- <!-- default: 'No results',-->
371
- <!-- },-->
372
- <!-- /**-->
373
- <!-- * Показывать ли состояние лоадинга-->
374
- <!-- */-->
375
- <!-- loading: {-->
376
- <!-- type: Boolean,-->
377
- <!-- default: false,-->
378
- <!-- },-->
379
- <!-- /**-->
380
- <!-- * Ограничить ли отображение выбранных опций-->
381
- <!-- */-->
382
- <!-- collapsed: {-->
383
- <!-- type: Boolean,-->
384
- <!-- default: false,-->
385
- <!-- },-->
386
- <!-- },-->
387
- <!-- data() {-->
388
- <!-- return {-->
389
- <!-- searchValue: null,-->
390
- <!-- key: `field_select_${Date.now()}`,-->
391
- <!-- field_key: `field-${this.name}`,-->
392
- <!-- closest_scroll_element: null,-->
393
- <!-- scroll_resize_observer: null,-->
394
- <!-- local_options: [],-->
395
- <!-- custom_limit: 0,-->
396
- <!-- is_show_all_options: false,-->
397
- <!-- }-->
398
- <!-- },-->
399
- <!-- computed: {-->
400
- <!-- tagBind() {-->
401
- <!-- return {-->
402
- <!-- label: 'name',-->
403
- <!-- trackBy: 'value',-->
404
- <!-- value: this._value,-->
405
- <!-- loading: this.loading,-->
406
- <!-- options: this.collapsed ? this.visibleOptions : this.computedOptions,-->
407
- <!-- searchable: this.searchable,-->
408
- <!-- showLabels: this.showLabels,-->
409
- <!-- multiple: this.multiple,-->
410
- <!-- hideSelected: this.hideSelected,-->
411
- <!-- allowEmpty: this.allowEmpty,-->
412
- <!-- openDirection: this.openDirection,-->
413
- <!-- id: this.name,-->
414
- <!-- taggable: this.taggable,-->
415
- <!-- tagPlaceholder: '',-->
416
- <!-- placeholder: this.placeholder,-->
417
- <!-- disabled: this.disabled,-->
418
- <!-- internalSearch: this.internalSearch,-->
419
- <!-- tabindex: +this.tabindex,-->
420
- <!-- ...(this.groupKeys ? { groupLabel: this.groupKeys.label } : {}),-->
421
- <!-- ...(this.groupKeys ? { groupValues: this.groupKeys.values } : {}),-->
422
- <!-- ...(this.collapsed ? { limit: this.is_show_all_options ? this.value?.length : this.custom_limit } : {}),-->
423
- <!-- class: this.collapsed ? 'mc-field-select__limit' : '',-->
424
- <!-- }-->
425
- <!-- },-->
426
- <!-- visibleOptions() {-->
427
- <!-- return this.is_show_all_options ? this.computedOptions : this.computedOptions.slice(0, this.custom_limit)-->
428
- <!-- },-->
429
- <!-- computedCaretClass() {-->
430
- <!-- return {-->
431
- <!-- multiselect__select: true,-->
432
- <!-- 'mc-field-select__limit-toggle': true,-->
433
- <!-- 'mc-field-select__limit-toggle&#45;&#45;close': this.is_show_all_options,-->
434
- <!-- }-->
435
- <!-- },-->
436
- <!-- limitText() {-->
437
- <!-- return `+${+this.value?.length - +this.custom_limit}`-->
438
- <!-- },-->
439
- <!-- isShowLimitToggle() {-->
440
- <!-- return this.collapsed && this.value?.length > this.custom_limit-->
441
- <!-- },-->
442
- <!-- hasTitle() {-->
443
- <!-- return !!this.title-->
444
- <!-- },-->
445
- <!-- /**-->
446
- <!-- * Если режим taggable && searchValueInOptions то добавлять введенный тег в опции-->
447
- <!-- * **/-->
448
- <!-- computedOptions() {-->
449
- <!-- let options = !this.groupKeys-->
450
- <!-- ? [...this.options, ...this.local_options].filter(-->
451
- <!-- (v, i, a) =>-->
452
- <!-- a.findIndex(afi => {-->
453
- <!-- if (typeof afi.value === 'object') {-->
454
- <!-- return JSON.stringify(afi.value) === JSON.stringify(v.value)-->
455
- <!-- }-->
456
- <!-- return String(afi.value) === String(v.value)-->
457
- <!-- }) === i,-->
458
- <!-- )-->
459
- <!-- : this.options-->
460
- <!-- if (this.searchValueInOptions && this.taggable) {-->
461
- <!-- const search = this.searchValue-->
462
- <!-- return search && search.length ? [{ name: search, value: search }, ...options] : options-->
463
- <!-- }-->
464
- <!-- return options-->
465
- <!-- },-->
466
- <!-- rtl() {-->
467
- <!-- return LANGUAGES.rtl.includes(this.locale)-->
468
- <!-- },-->
469
- <!-- dir() {-->
470
- <!-- return this.rtl ? 'rtl' : null-->
471
- <!-- },-->
472
- <!-- classes() {-->
473
- <!-- return {-->
474
- <!-- 'mc-field-select': true,-->
475
- <!-- 'mc-field-select&#45;&#45;error': this.errorText,-->
476
- <!-- 'mc-field-select&#45;&#45;disabled': this.disabled,-->
477
- <!-- [`mc-field-select&#45;&#45;bg-${this.backgroundColor}`]: this.backgroundColor,-->
478
- <!-- 'mc-field-select&#45;&#45;is-empty-options-list': this.isEmptyOptions,-->
479
- <!-- 'mc-field-select&#45;&#45;with-preview': this.optionWithPreview,-->
480
- <!-- 'mc-field-select&#45;&#45;hide-arrow': !!this.$slots.arrow,-->
481
- <!-- 'mc-field-select&#45;&#45;grouped': this.groupKeys,-->
482
- <!-- 'mc-field-select&#45;&#45;max-height': this.maxHeight,-->
483
- <!-- 'mc-field-select&#45;&#45;empty': !this._value,-->
484
- <!-- 'mc-field-select&#45;&#45;rtl': this.rtl,-->
485
- <!-- }-->
486
- <!-- },-->
487
- <!-- isEmptyOptions() {-->
488
- <!-- let options = this.computedOptions-->
489
- <!-- if (this.groupKeys) {-->
490
- <!-- options = []-->
491
- <!-- for (let option of this.computedOptions) {-->
492
- <!-- options.push(...option[this.groupKeys.values])-->
493
- <!-- }-->
494
- <!-- }-->
495
- <!-- return this.isEmptyOptionsList || this.loading || options?.length === this._value?.length-->
496
- <!-- },-->
497
- <!-- computedTitle() {-->
498
- <!-- return `${this.title}${this.required ? ' *' : ''}`-->
499
- <!-- },-->
500
- <!-- styles() {-->
501
- <!-- const darkColors = ['gray', 'dark-gray', 'black']-->
502
- <!-- const lightColors = ['white']-->
503
- <!-- let placeHolderColor-->
504
- <!-- let borderColor = this.backgroundColor-->
505
- <!-- let backgroundColor = this.backgroundColor-->
506
- <!-- let labelColor-->
507
- <!-- if (!this.backgroundColor || lightColors.includes(this.backgroundColor)) {-->
508
- <!-- borderColor = 'main'-->
509
- <!-- }-->
510
- <!-- if (darkColors.includes(this.backgroundColor)) {-->
511
- <!-- labelColor = 'white'-->
512
- <!-- placeHolderColor = 'white'-->
513
- <!-- borderColor = 'black'-->
514
- <!-- }-->
515
- <!-- if (this.disabled && !this.backgroundColor) {-->
516
- <!-- backgroundColor = 'hover-gray'-->
517
- <!-- }-->
518
- <!-- return {-->
519
- <!-- '&#45;&#45;mc-field-select-max-height': this.maxHeight,-->
520
- <!-- '&#45;&#45;mc-field-select-color': backgroundColor && `var(&#45;&#45;color-${backgroundColor})`,-->
521
- <!-- '&#45;&#45;mc-field-select-border-color': borderColor && `var(&#45;&#45;color-${borderColor})`,-->
522
- <!-- '&#45;&#45;mc-field-select-label-color': labelColor && `var(&#45;&#45;color-${labelColor})`,-->
523
- <!-- '&#45;&#45;mc-field-select-placeholder-color': placeHolderColor && `var(&#45;&#45;color-${placeHolderColor})`,-->
524
- <!-- }-->
525
- <!-- },-->
526
- <!-- _value() {-->
527
- <!-- if (this.multiple) {-->
528
- <!-- if (this.value === null) return []-->
529
- <!-- let result = []-->
530
- <!-- for (let value of this.value) {-->
531
- <!-- const options = [-->
532
- <!-- ...(this.groupKeys-->
533
- <!-- ? this.options.map(o => o[this.groupKeys.values]).flat()-->
534
- <!-- : this.computedOptions),-->
535
- <!-- ]-->
536
- <!-- let option = options.find(o => {-->
537
- <!-- if (o.value?.hasOwnProperty('id') && o.value.id == value.id) {-->
538
- <!-- return true-->
539
- <!-- }-->
540
- <!-- return o.value == value-->
541
- <!-- })-->
542
- <!-- if (option !== null) result.push(option)-->
543
- <!-- }-->
544
- <!-- return result-->
545
- <!-- }-->
546
- <!-- if (this.groupKeys) {-->
547
- <!-- let ungruppedOptions = []-->
548
- <!-- for (let option of this.options) {-->
549
- <!-- ungruppedOptions.push(...option[this.groupKeys.values])-->
550
- <!-- }-->
551
- <!-- return ungruppedOptions.find(o => o.value == this.value)-->
552
- <!-- }-->
553
- <!-- return this.computedOptions.find(o => o.value == this.value)-->
554
- <!-- },-->
555
-
556
- <!-- isEmptyOptionsList() {-->
557
- <!-- if ((this.hideSelected && !this.searchValue) || !this.options.length) {-->
558
- <!-- if (this.multiple) {-->
559
- <!-- if (this.groupKeys) return false-->
560
- <!-- return this.options.length === this._value.length-->
561
- <!-- } else {-->
562
- <!-- return this._value && this.computedOptions.length === 1 && !this.searchValue-->
563
- <!-- }-->
564
- <!-- } else if (this.options.length === 0) return !this.options.length-->
565
- <!-- return false-->
566
- <!-- },-->
567
- <!-- hasPrepend() {-->
568
- <!-- return this.avatar || this.icon-->
569
- <!-- },-->
570
- <!-- },-->
571
- <!-- watch: {-->
572
- <!-- options: {-->
573
- <!-- immediate: true,-->
574
- <!-- handler(val) {-->
575
- <!-- //Пушим все входящие опции в локальные опции-->
576
- <!-- this.local_options.push(...val)-->
577
- <!-- this.actualizeSavedOptions()-->
578
- <!-- this.calcLimit()-->
579
- <!-- },-->
580
- <!-- },-->
581
- <!-- value: {-->
582
- <!-- deep: true,-->
583
- <!-- immediate: true,-->
584
- <!-- handler() {-->
585
- <!-- this.actualizeSavedOptions()-->
586
- <!-- this.calcLimit()-->
587
- <!-- },-->
588
- <!-- },-->
589
- <!-- },-->
590
- <!-- methods: {-->
591
- <!-- actualizeSavedOptions() {-->
592
- <!-- //Фильтруем локальные опции и оставляем только те, значения которых выбраны в селекте-->
593
- <!-- this.local_options = this.local_options.filter(lo =>-->
594
- <!-- this.value?.constructor === Array-->
595
- <!-- ? this.value.map(v => String(v)).includes(String(lo.value))-->
596
- <!-- : String(lo.value) === String(this.value),-->
597
- <!-- )-->
598
-
599
- <!-- //Делаем Юник, что бы опции не повторялись-->
600
- <!-- this.local_options = this.local_options.filter(-->
601
- <!-- (v, i, a) =>-->
602
- <!-- a.findIndex(afi => {-->
603
- <!-- if (typeof afi.value === 'object') {-->
604
- <!-- return JSON.stringify(afi.value) === JSON.stringify(v.value)-->
605
- <!-- }-->
606
- <!-- return String(afi.value) === String(v.value)-->
607
- <!-- }) === i,-->
608
- <!-- )-->
609
- <!-- },-->
610
- <!-- handleOpen() {-->
611
- <!-- if (!this.renderAbsoluteList) return-->
612
- <!-- this.initScroll()-->
613
- <!-- },-->
614
- <!-- handleClose() {-->
615
- <!-- this.closest_scroll_element?.removeEventListener('scroll', this.repositionDropDown)-->
616
- <!-- this.scroll_resize_observer?.disconnect()-->
617
- <!-- this.scroll_resize_observer = null-->
618
- <!-- },-->
619
- <!-- findClosestScrollElement(element) {-->
620
- <!-- if (!element) return document.documentElement-->
621
- <!-- const { overflow, overflowY } = getComputedStyle(element)-->
622
- <!-- const scrollableVariants = ['auto', 'scroll']-->
623
- <!-- return scrollableVariants.some(v => [overflow, overflowY].includes(v))-->
624
- <!-- ? element-->
625
- <!-- : this.findClosestScrollElement(element.parentNode)-->
626
- <!-- },-->
627
- <!-- initScroll() {-->
628
- <!-- // looking for closest scroll elemen to track select list position dynamically-->
629
- <!-- this.closest_scroll_element = this.findClosestScrollElement(this.$refs[this.field_key])-->
630
- <!-- this.closest_scroll_element.addEventListener('scroll', this.repositionDropDown)-->
631
- <!-- this.scroll_resize_observer = new ResizeObserver(this.repositionDropDown)-->
632
- <!-- this.scroll_resize_observer.observe(this.closest_scroll_element)-->
633
- <!-- },-->
634
- <!-- repositionDropDown() {-->
635
- <!-- const { top, bottom, height, width, left } = this.$el.getBoundingClientRect()-->
636
- <!-- const scrollElementRect = this.closest_scroll_element.getBoundingClientRect()-->
637
- <!-- const ref = this.$refs[this.key]-->
638
- <!-- if (!ref) return-->
639
- <!-- const ios_devices = ['iPhone', 'iPad']-->
640
- <!-- // Добавляем к позиции отступ visualViewport?.offsetTop, который добавляет iOs при открытии вирутальной клавиатуры-->
641
- <!-- const iosViewportIndent = ios_devices?.some(device => navigator?.platform?.includes(device))-->
642
- <!-- ? window.visualViewport?.offsetTop || 0-->
643
- <!-- : 0-->
644
- <!-- // Высчитываем реальную позицию селекта относительно первого скроллящегося родителя-->
645
- <!-- const actualTop = top - scrollElementRect.top-->
646
- <!-- const actualBottom = bottom - scrollElementRect.bottom-->
647
- <!-- // if field hides under scrolled element borders -> blur select to prevent overlap-->
648
- <!-- if (actualTop >= -height && actualBottom < height) {-->
649
- <!-- ref.$refs.list.style.width = `${width}px`-->
650
- <!-- ref.$refs.list.style.position = 'fixed'-->
651
- <!-- ref.$refs.list.style.left = `${left}px`-->
652
- <!-- const title_height = document.querySelector('.mc-field-select__header').offsetHeight-->
653
- <!-- const title_margin = 8-->
654
- <!-- let openDirection = this.openDirection-->
655
- <!-- if (openDirection === 'auto') openDirection = ref?.isAbove ? 'top' : 'bottom'-->
656
- <!-- switch (openDirection) {-->
657
- <!-- case 'top':-->
658
- <!-- ref.$refs.list.style.top = `${top +-->
659
- <!-- (this.hasTitle ? title_height + title_margin : 0) +-->
660
- <!-- iosViewportIndent - -->
661
- <!-- ref.$refs.list.getBoundingClientRect().height - -->
662
- <!-- 8}px`-->
663
- <!-- ref.$refs.list.style.bottom = 'auto'-->
664
- <!-- break-->
665
- <!-- case 'bottom':-->
666
- <!-- ref.$refs.list.style.bottom = 'auto'-->
667
- <!-- ref.$refs.list.style.top = `${top + iosViewportIndent + height}px`-->
668
- <!-- break-->
669
- <!-- }-->
670
- <!-- // Для андроидов не прячем селект при оверлапе, так как там работает все криво-->
671
- <!-- // при открытии из нижней позиции клавиатура его перекрывает и он сразу сам закрывается-->
672
- <!-- } else if (!/Android/i.test(navigator.userAgent)) {-->
673
- <!-- // прячем селект, если его не видно юзеру-->
674
- <!-- return ref.deactivate()-->
675
- <!-- }-->
676
- <!-- },-->
677
- <!-- handleChange(value) {-->
678
- <!-- /**-->
679
- <!-- * Истинное значение инпута-->
680
- <!-- */-->
681
- <!-- this.$emit('original-input', value)-->
682
- <!-- if (value !== null) {-->
683
- <!-- if (this.multiple) {-->
684
- <!-- value = value.map(v => v.value)-->
685
- <!-- } else {-->
686
- <!-- value = value.value-->
687
- <!-- }-->
688
- <!-- }-->
689
- <!-- this.emitInput(value)-->
690
- <!-- },-->
691
-
692
- <!-- handleTag(value) {-->
693
- <!-- /**-->
694
- <!-- * Событие по добавлению-->
695
- <!-- * тега в инпут (по Enter)-->
696
- <!-- * @property {string}-->
697
- <!-- */-->
698
- <!-- this.$emit('tag', value)-->
699
- <!-- },-->
700
-
701
- <!-- handleSearchChange(value) {-->
702
- <!-- this.searchValue = value-->
703
- <!-- /**-->
704
- <!-- * Событие по вводу данных в инпут-->
705
- <!-- * @property {string}-->
706
- <!-- */-->
707
- <!-- this.$emit('search-change', value)-->
708
- <!-- this.renderAbsoluteList && this.$nextTick(() => this.repositionDropDown())-->
709
- <!-- },-->
710
-
711
- <!-- emitInput(value) {-->
712
- <!-- this.toggleErrorVisible()-->
713
- <!-- /**-->
714
- <!-- * Событие инпута (выбранное значение)-->
715
- <!-- * @property {array, number}-->
716
- <!-- */-->
717
- <!-- this.$emit('input', value)-->
718
- <!-- },-->
719
- <!-- /**-->
720
- <!-- * Вычисляем custom_limit, которое ограничивает кол-во дочерних элементов внутри родительского, чтобы они не превышали его ширину-->
721
- <!-- * */-->
722
- <!-- calcLimit() {-->
723
- <!-- if (!this.collapsed) return-->
724
- <!-- this.$nextTick(() => {-->
725
- <!-- this.custom_limit = Infinity-->
726
- <!-- let child_width = 0-->
727
- <!-- const parent = this.$refs[this.key]?.$refs?.tags?.firstChild-->
728
- <!-- if (!this.value?.length) return-->
729
- <!-- const limit_text_width = this.getLimitTextWidth() // Получаем ширину текста лимита-->
730
- <!-- const total_width = +parent?.clientWidth - +limit_text_width-->
731
- <!-- for (let i = 0; i < this.value?.length; i++) {-->
732
- <!-- const children = parent?.children?.[i]-->
733
- <!-- const elem_style = window.getComputedStyle(children)-->
734
- <!-- child_width += children?.clientWidth + (parseInt(elem_style?.marginRight) || 0)-->
735
- <!-- // считаем занимаемую дочерними элементами ширину, если превышает родительскую, то выходим из цикла и ставим лимит-->
736
- <!-- if (+child_width > +total_width) {-->
737
- <!-- this.custom_limit = i-->
738
- <!-- break-->
739
- <!-- }-->
740
- <!-- }-->
741
- <!-- })-->
742
- <!-- },-->
743
- <!-- getLimitTextWidth() {-->
744
- <!-- const temp_limit_element = document.createElement('div')-->
745
- <!-- temp_limit_element.style.visibility = 'hidden'-->
746
- <!-- temp_limit_element.style.position = 'absolute'-->
747
- <!-- temp_limit_element.innerText = `+${this.value?.length}` // Устанавливаем текст лимита-->
748
- <!-- document.body.appendChild(temp_limit_element)-->
749
- <!-- const limit_text_width = temp_limit_element.clientWidth-->
750
- <!-- document.body.removeChild(temp_limit_element)-->
751
- <!-- return limit_text_width-->
752
- <!-- },-->
753
- <!-- toggleOptions() {-->
754
- <!-- this.is_show_all_options = !this.is_show_all_options-->
755
- <!-- },-->
756
- <!-- },-->
757
- <!--}-->
758
- <!--</script>-->
759
-
760
- <!--<style lang="scss">-->
761
- <!--@import 'vue-multiselect/dist/vue-multiselect.min';-->
762
- <!--@import '../../../styles/mixins';-->
763
- <!--@import '../../../tokens/durations';-->
764
- <!--@import '../../../tokens/border-radius';-->
765
- <!--@import '../../../tokens/font-families';-->
766
- <!--@import '../../../tokens/box-shadows';-->
767
- <!--@import '../../../tokens/colors';-->
768
- <!--@import '../../../tokens/font-sizes';-->
769
- <!--@import '../../../tokens/line-heights';-->
770
- <!--@import '../../../tokens/font-weights';-->
771
- <!--@import '../../../tokens/sizes';-->
772
- <!--@import '../../../tokens/spacings';-->
773
-
774
- <!--.mc-field-select {-->
775
- <!-- $block-name: &;-->
776
- <!-- &#45;&#45;mc-field-select-color: initial;-->
777
- <!-- &#45;&#45;mc-field-select-label-color: #{$color-black};-->
778
- <!-- &#45;&#45;mc-field-select-border-color: initial;-->
779
- <!-- &#45;&#45;mc-field-select-max-height: initial;-->
780
- <!-- &#45;&#45;mc-field-select-placeholder-color: #{$color-gray};-->
781
- <!-- @include custom-scroll($space-100);-->
782
- <!-- font-family: $font-family-main;-->
783
-
784
- <!-- &__header {-->
785
- <!-- @include reset-text-indents();-->
786
- <!-- display: block;-->
787
- <!-- margin-bottom: $space-100;-->
788
-
789
- <!-- &:empty {-->
790
- <!-- display: none;-->
791
- <!-- }-->
792
- <!-- }-->
793
-
794
- <!-- &__footer {-->
795
- <!-- margin-top: $space-50;-->
796
- <!-- line-height: $line-height-150;-->
797
-
798
- <!-- &:empty {-->
799
- <!-- display: none;-->
800
- <!-- }-->
801
- <!-- }-->
802
-
803
- <!-- &__single-label {-->
804
- <!-- @include reset-text-indents();-->
805
- <!-- position: relative;-->
806
- <!-- display: flex;-->
807
- <!-- flex-wrap: nowrap;-->
808
- <!-- align-items: center;-->
809
- <!-- @include child-indent-right($space-50);-->
810
- <!-- }-->
811
-
812
- <!-- &__prepend {-->
813
- <!-- position: absolute;-->
814
- <!-- }-->
815
-
816
- <!-- &__label-text {-->
817
- <!-- @include ellipsis();-->
818
- <!-- font-size: $font-size-200;-->
819
- <!-- line-height: $line-height-200;-->
820
- <!-- padding-inline-start: $space-50;-->
821
- <!-- color: var(&#45;&#45;mc-field-select-label-color);-->
822
-
823
- <!-- &&#45;&#45;indent-left {-->
824
- <!-- margin-inline-start: $space-300;-->
825
- <!-- }-->
826
- <!-- }-->
827
-
828
- <!-- .multiselect {-->
829
- <!-- &__placeholder {-->
830
- <!-- @include ellipsis();-->
831
- <!-- color: var(&#45;&#45;mc-field-select-placeholder-color);-->
832
- <!-- font-size: $font-size-200;-->
833
- <!-- line-height: $line-height-200;-->
834
- <!-- margin-bottom: $space-150 - 1px;-->
835
- <!-- padding-top: $space-150 - 1px;-->
836
- <!-- padding-inline-start: $space-50;-->
837
- <!-- width: 100%;-->
838
- <!-- }-->
839
-
840
- <!-- &__single {-->
841
- <!-- padding-inline-start: 0;-->
842
- <!-- margin-bottom: $space-150 - 1px;-->
843
- <!-- margin-top: $space-150 - 1px;-->
844
- <!-- background-color: transparent;-->
845
- <!-- min-height: auto;-->
846
-
847
- <!-- @include input-placeholder() {-->
848
- <!-- color: $color-gray;-->
849
- <!-- }-->
850
- <!-- }-->
851
- <!-- &__tag {-->
852
- <!-- padding-left: 2px !important;-->
853
- <!-- .mc-chip__button {-->
854
- <!-- background-color: $color-white;-->
855
- <!-- border-radius: $radius-circle;-->
856
- <!-- opacity: 1;-->
857
- <!-- .mc-svg-icon {-->
858
- <!-- min-width: $space-250;-->
859
- <!-- width: $space-250;-->
860
- <!-- min-height: $space-250;-->
861
- <!-- height: $space-250;-->
862
- <!-- }-->
863
- <!-- &:hover {-->
864
- <!-- .mc-svg-icon {-->
865
- <!-- fill: var(&#45;&#45;color-red);-->
866
- <!-- }-->
867
- <!-- }-->
868
- <!-- .mc-svg-icon {-->
869
- <!-- fill: var(&#45;&#45;color-main);-->
870
- <!-- }-->
871
- <!-- }-->
872
- <!-- }-->
873
- <!-- &__input {-->
874
- <!-- padding-inline-start: $space-50;-->
875
- <!-- margin-bottom: $space-150 - 2px;-->
876
- <!-- padding-top: $space-150 - 1px;-->
877
- <!-- font-size: $font-size-200;-->
878
- <!-- line-height: $line-height-200;-->
879
- <!-- min-height: auto;-->
880
- <!-- background-color: $color-transparent;-->
881
- <!-- @include input-placeholder() {-->
882
- <!-- color: $color-gray;-->
883
- <!-- }-->
884
- <!-- }-->
885
-
886
- <!-- &__select {-->
887
- <!-- overflow: hidden;-->
888
- <!-- height: $space-350;-->
889
- <!-- width: $space-300;-->
890
- <!-- inset-inline-end: $space-100;-->
891
- <!-- top: 6px;-->
892
- <!-- padding: 0;-->
893
- <!-- z-index: 1;-->
894
-
895
- <!-- &::before {-->
896
- <!-- direction: ltr;-->
897
- <!-- width: 0;-->
898
- <!-- height: 0;-->
899
- <!-- border-left: 5px solid transparent;-->
900
- <!-- border-right: 5px solid transparent;-->
901
- <!-- border-bottom: 5px solid transparent;-->
902
- <!-- border-top: 5px solid var(&#45;&#45;mc-field-select-label-color);-->
903
- <!-- }-->
904
- <!-- }-->
905
-
906
- <!-- &__tags {-->
907
- <!-- @include reset-text-indents();-->
908
- <!-- position: relative;-->
909
- <!-- border: 1px solid $color-outline-gray;-->
910
- <!-- border-radius: $radius-100 !important;-->
911
- <!-- padding: 0;-->
912
- <!-- padding-inline: $space-100 $space-500;-->
913
- <!-- overflow: hidden;-->
914
- <!-- text-align: start;-->
915
-
916
- <!-- &:hover {-->
917
- <!-- border-color: var(&#45;&#45;color-main);-->
918
- <!-- }-->
919
-
920
- <!-- &:before {-->
921
- <!-- content: '';-->
922
- <!-- position: absolute;-->
923
- <!-- left: 0;-->
924
- <!-- top: 0;-->
925
- <!-- @include size(100%);-->
926
- <!-- background-color: var(&#45;&#45;mc-field-select-color);-->
927
- <!-- opacity: 0.6;-->
928
- <!-- }-->
929
- <!-- }-->
930
-
931
- <!-- &__tags-wrap {-->
932
- <!-- width: 100%;-->
933
- <!-- position: relative;-->
934
- <!-- padding-top: 4px;-->
935
- <!-- padding-bottom: 3px;-->
936
- <!-- top: 0;-->
937
- <!-- display: flex;-->
938
- <!-- flex-wrap: wrap;-->
939
- <!-- margin-top: -1px;-->
940
- <!-- min-height: $size-500 - 2px;-->
941
- <!-- @include child-indent-right($space-100);-->
942
- <!-- }-->
943
-
944
- <!-- &__tag {-->
945
- <!-- display: inline-flex;-->
946
- <!-- align-items: center;-->
947
- <!-- height: $size-300;-->
948
- <!-- font-family: $font-family-main;-->
949
- <!-- margin-top: $space-50;-->
950
- <!-- margin-bottom: $space-50;-->
951
- <!-- margin-right: unset;-->
952
- <!-- background-color: var(&#45;&#45;color-main-alpha-10);-->
953
- <!-- color: $color-black;-->
954
- <!-- padding: $size-50 $size-50 $size-50 $size-100;-->
955
- <!-- border-radius: 100px;-->
956
- <!-- font-size: $font-size-200;-->
957
- <!-- line-height: $line-height-200;-->
958
-
959
- <!-- span {-->
960
- <!-- @include ellipsis();-->
961
- <!-- flex: 1 1 auto;-->
962
- <!-- //overflow: visible;-->
963
- <!-- }-->
964
- <!-- }-->
965
-
966
- <!-- &__tag-icon {-->
967
- <!-- @include size($size-200);-->
968
- <!-- position: relative;-->
969
- <!-- background-color: var(&#45;&#45;color-main);-->
970
- <!-- border-radius: $radius-circle;-->
971
- <!-- flex: 0 0 auto;-->
972
- <!-- margin-inline-start: $space-100;-->
973
-
974
- <!-- &:hover {-->
975
- <!-- background-color: $color-red;-->
976
- <!-- }-->
977
-
978
- <!-- &::after {-->
979
- <!-- @include align(true, true, absolute);-->
980
- <!-- top: 45%;-->
981
- <!-- color: $color-white;-->
982
- <!-- }-->
983
- <!-- }-->
984
-
985
- <!-- &__content {-->
986
- <!-- padding: $size-100;-->
987
- <!-- max-width: 100%;-->
988
- <!-- font-size: $font-size-200;-->
989
- <!-- line-height: $line-height-200;-->
990
- <!-- }-->
991
-
992
- <!-- &__content-wrapper {-->
993
- <!-- top: calc(100% + #{$size-100});-->
994
- <!-- border: none;-->
995
- <!-- border-radius: $radius-150;-->
996
- <!-- box-shadow: $shadow-s;-->
997
- <!-- transition: opacity $duration-s ease;-->
998
- <!-- overflow-y: auto;-->
999
- <!-- overflow-x: hidden;-->
1000
- <!-- max-height: 300px;-->
1001
- <!-- }-->
1002
-
1003
- <!-- &&#45;&#45;above {-->
1004
- <!-- .multiselect__content-wrapper {-->
1005
- <!-- bottom: calc(100% + #{$size-100});-->
1006
- <!-- top: auto;-->
1007
- <!-- }-->
1008
- <!-- }-->
1009
-
1010
- <!-- &__option {-->
1011
- <!-- min-height: $size-500;-->
1012
- <!-- display: flex;-->
1013
- <!-- align-items: center;-->
1014
- <!-- border-radius: $radius-100;-->
1015
- <!-- padding: $space-150;-->
1016
-
1017
- <!-- span {-->
1018
- <!-- @include ellipsis();-->
1019
- <!-- }-->
1020
-
1021
- <!-- &&#45;&#45;highlight {-->
1022
- <!-- background-color: $color-hover-gray;-->
1023
- <!-- color: $color-black;-->
1024
- <!-- }-->
1025
-
1026
- <!-- &&#45;&#45;selected {-->
1027
- <!-- background-color: var(&#45;&#45;color-main-alpha-10) !important;-->
1028
- <!-- color: $color-black !important;-->
1029
- <!-- font-weight: $font-weight-medium;-->
1030
- <!-- }-->
1031
-
1032
- <!-- &&#45;&#45;group.multiselect__option&#45;&#45;disabled {-->
1033
- <!-- background-color: $color-white !important;-->
1034
- <!-- }-->
1035
-
1036
- <!-- .option__desc {-->
1037
- <!-- .mc-preview__bottom {-->
1038
- <!-- margin-top: 0;-->
1039
- <!-- }-->
1040
- <!-- }-->
1041
- <!-- }-->
1042
-
1043
- <!-- &&#45;&#45;active {-->
1044
- <!-- .multiselect {-->
1045
- <!-- &__tags {-->
1046
- <!-- &:before {-->
1047
- <!-- background-color: $color-transparent;-->
1048
- <!-- }-->
1049
-
1050
- <!-- border-color: var(&#45;&#45;mc-field-select-border-color);-->
1051
- <!-- }-->
1052
-
1053
- <!-- &__select {-->
1054
- <!-- &::before {-->
1055
- <!-- border-color: var(&#45;&#45;color-main) $color-transparent $color-transparent;-->
1056
- <!-- }-->
1057
- <!-- }-->
1058
- <!-- }-->
1059
- <!-- }-->
1060
-
1061
- <!-- &__spinner {-->
1062
- <!-- &:after,-->
1063
- <!-- &:before {-->
1064
- <!-- border-top-color: var(&#45;&#45;color-main);-->
1065
- <!-- @include size($space-300);-->
1066
- <!-- top: calc(50% - 5px);-->
1067
- <!-- left: calc(50% - 2px);-->
1068
- <!-- }-->
1069
- <!-- }-->
1070
- <!-- }-->
1071
-
1072
- <!-- &&#45;&#45;error {-->
1073
- <!-- .multiselect {-->
1074
- <!-- &__tags {-->
1075
- <!-- border-color: $color-red !important;-->
1076
- <!-- }-->
1077
- <!-- }-->
1078
- <!-- }-->
1079
-
1080
- <!-- &&#45;&#45;hide-arrow {-->
1081
- <!-- .multiselect {-->
1082
- <!-- &__select {-->
1083
- <!-- display: none !important;-->
1084
- <!-- }-->
1085
- <!-- }-->
1086
- <!-- }-->
1087
-
1088
- <!-- &&#45;&#45;is-empty-options-list {-->
1089
- <!-- .multiselect {-->
1090
- <!-- &__content-wrapper {-->
1091
- <!-- display: none !important;-->
1092
- <!-- }-->
1093
- <!-- }-->
1094
- <!-- }-->
1095
-
1096
- <!-- &&#45;&#45;disabled {-->
1097
- <!-- cursor: not-allowed;-->
1098
-
1099
- <!-- .multiselect&#45;&#45;disabled {-->
1100
- <!-- opacity: 1;-->
1101
- <!-- background: transparent;-->
1102
-
1103
- <!-- .multiselect {-->
1104
- <!-- &__placeholder {-->
1105
- <!-- color: $color-gray;-->
1106
- <!-- }-->
1107
-
1108
- <!-- &__single {-->
1109
- <!-- & #{$block-name}__label-text {-->
1110
- <!-- color: $color-gray;-->
1111
- <!-- }-->
1112
- <!-- }-->
1113
-
1114
- <!-- &__select {-->
1115
- <!-- background-color: transparent;-->
1116
-
1117
- <!-- &::before {-->
1118
- <!-- border-color: $color-outline-gray transparent transparent;-->
1119
- <!-- }-->
1120
- <!-- }-->
1121
- <!-- }-->
1122
- <!-- }-->
1123
- <!-- }-->
1124
-
1125
- <!-- &&#45;&#45;with-preview {-->
1126
- <!-- .mc-preview {-->
1127
- <!-- align-items: center;-->
1128
- <!-- }-->
1129
-
1130
- <!-- &#{$block-name} {-->
1131
- <!-- &&#45;&#45;grouped {-->
1132
- <!-- .multiselect {-->
1133
- <!-- &__content {-->
1134
- <!-- padding: $space-200;-->
1135
- <!-- }-->
1136
- <!-- }-->
1137
- <!-- }-->
1138
-
1139
- <!-- &&#45;&#45;empty {-->
1140
- <!-- .multiselect {-->
1141
- <!-- &__tags {-->
1142
- <!-- padding: 0;-->
1143
- <!-- border-radius: $radius-100 !important;-->
1144
- <!-- }-->
1145
-
1146
- <!-- &__placeholder {-->
1147
- <!-- padding-inline-start: $space-150;-->
1148
- <!-- }-->
1149
-
1150
- <!-- &__select {-->
1151
- <!-- display: block;-->
1152
- <!-- }-->
1153
- <!-- }-->
1154
- <!-- }-->
1155
- <!-- }-->
1156
-
1157
- <!-- .multiselect {-->
1158
- <!-- &__content {-->
1159
- <!-- padding: 0;-->
1160
- <!-- }-->
1161
-
1162
- <!-- &&#45;&#45;active {-->
1163
- <!-- .multiselect__select {-->
1164
- <!-- transform: translateY(-50%) rotate(180deg);-->
1165
- <!-- }-->
1166
- <!-- }-->
1167
-
1168
- <!-- &__select {-->
1169
- <!-- top: 50%;-->
1170
- <!-- transform: translateY(-50%);-->
1171
- <!-- }-->
1172
-
1173
- <!-- &__single {-->
1174
- <!-- margin: 0;-->
1175
- <!-- }-->
1176
-
1177
- <!-- &__tags {-->
1178
- <!-- padding: $space-200 0;-->
1179
- <!-- padding-inline: $space-150;-->
1180
- <!-- cursor: pointer;-->
1181
- <!-- border-color: $color-outline-light;-->
1182
- <!-- }-->
1183
-
1184
- <!-- &__option,-->
1185
- <!-- &__content-wrapper,-->
1186
- <!-- &__tags {-->
1187
- <!-- border-radius: $radius-200 !important;-->
1188
- <!-- }-->
1189
-
1190
- <!-- &__option {-->
1191
- <!-- padding: $space-200 $space-150;-->
1192
-
1193
- <!-- &&#45;&#45;group {-->
1194
- <!-- padding: 0 0 $space-100 0;-->
1195
- <!-- min-height: auto;-->
1196
- <!-- }-->
1197
- <!-- }-->
1198
-
1199
- <!-- &__element {-->
1200
- <!-- &:not([role='option']) {-->
1201
- <!-- &:not(:first-of-type) {-->
1202
- <!-- margin-top: $space-300;-->
1203
- <!-- }-->
1204
- <!-- }-->
1205
- <!-- }-->
1206
- <!-- }-->
1207
- <!-- }-->
1208
-
1209
- <!-- &&#45;&#45;max-height {-->
1210
- <!-- .multiselect {-->
1211
- <!-- &__tags {-->
1212
- <!-- max-height: var(&#45;&#45;mc-field-select-max-height);-->
1213
- <!-- overflow-y: auto;-->
1214
- <!-- position: initial;-->
1215
- <!-- }-->
1216
-
1217
- <!-- &__spinner {-->
1218
- <!-- background: transparent;-->
1219
- <!-- right: calc(#{$space-50} / 2);-->
1220
- <!-- top: calc(#{$space-50} / 2);-->
1221
- <!-- }-->
1222
- <!-- }-->
1223
- <!-- }-->
1224
-
1225
- <!-- &__options-tooltip-target {-->
1226
- <!-- overflow: hidden;-->
1227
- <!-- text-overflow: ellipsis;-->
1228
- <!-- white-space: nowrap;-->
1229
- <!-- }-->
1230
-
1231
- <!-- &&#45;&#45;rtl {-->
1232
- <!-- direction: rtl;-->
1233
- <!-- }-->
1234
-
1235
- <!-- &__limit {-->
1236
- <!-- &-text {-->
1237
- <!-- position: relative;-->
1238
- <!-- z-index: 1;-->
1239
- <!-- width: auto;-->
1240
- <!-- }-->
1241
-
1242
- <!-- &-toggle {-->
1243
- <!-- pointer-events: auto;-->
1244
-
1245
- <!-- &:before {-->
1246
- <!-- border-color: black transparent transparent !important;-->
1247
- <!-- }-->
1248
-
1249
- <!-- &&#45;&#45;close {-->
1250
- <!-- transform: rotate(180deg);-->
1251
- <!-- }-->
1252
- <!-- }-->
1253
-
1254
- <!-- .multiselect__tags {-->
1255
- <!-- display: flex;-->
1256
- <!-- justify-content: space-between;-->
1257
- <!-- align-items: center;-->
1258
- <!-- }-->
1259
- <!-- }-->
1260
- <!--}-->
1261
- <!--</style>-->
1
+ <template>
2
+ <div :ref="field_key" :dir="dir" :class="classes" :style="styles">
3
+ <div :for="name" class="mc-field-select__header">
4
+ <!-- @slot Слот заголовка -->
5
+ <slot name="header">
6
+ <mc-title v-if="hasTitle" :ellipsis="false" max-width="100%" weight="medium">
7
+ {{ computedTitle }}
8
+ </mc-title>
9
+ </slot>
10
+ </div>
11
+ <div class="mc-field-select__main">
12
+ <multi-select
13
+ :ref="key"
14
+ v-bind="tagBind"
15
+ @input="handleChange"
16
+ @tag="handleTag"
17
+ @search-change="handleSearchChange"
18
+ @open="handleOpen"
19
+ @close="handleClose"
20
+ >
21
+ <template v-if="isShowLimitToggle" slot="caret">
22
+ <div :class="computedCaretClass" @click="toggleOptions" />
23
+ </template>
24
+ <template v-if="collapsed && !is_show_all_options" slot="limit">
25
+ <mc-title variation="body" class="mc-field-select__limit-text">
26
+ {{ limitText }}
27
+ </mc-title>
28
+ </template>
29
+ <template slot="singleLabel" slot-scope="{ option }">
30
+ <mc-preview v-if="optionWithPreview" class="option__desc" size="l">
31
+ <template v-if="option?.icon || option?.image" #left>
32
+ <mc-avatar v-if="option.image" :src="option.image" size="400" />
33
+ <mc-svg-icon v-else :name="option.icon" :color="option.iconColor || 'main'" size="400" />
34
+ </template>
35
+ <mc-title slot="top" weight="semi-bold" v-html="option.name" />
36
+ <!-- Слот для замены стандартной стрелки при выведенном превью -->
37
+ <slot slot="right" name="arrow" />
38
+ <mc-title slot="bottom" color="gray">
39
+ {{ option.text }}
40
+ </mc-title>
41
+ </mc-preview>
42
+ <div v-else class="mc-field-select__single-label">
43
+ <div v-if="hasPrepend" class="mc-field-select__prepend">
44
+ <mc-avatar v-if="avatar" :src="avatar" />
45
+ <mc-svg-icon v-else :name="icon" />
46
+ </div>
47
+ <div
48
+ class="mc-field-select__label-text"
49
+ :class="hasPrepend ? 'mc-field-select__label-text--indent-left' : ''"
50
+ >
51
+ {{ option ? option.name : placeholder }}
52
+ </div>
53
+ </div>
54
+ </template>
55
+ <template slot="tag" slot-scope="{ option, remove }">
56
+ <mc-chip
57
+ variation="main-alpha-10-invert"
58
+ class="multiselect__tag"
59
+ text-color="black"
60
+ size="s"
61
+ :closable="!option.hasOwnProperty('is_closable') || option.is_closable"
62
+ @click="remove(option)"
63
+ >
64
+ {{ option.name }}
65
+ </mc-chip>
66
+ </template>
67
+ <template
68
+ v-if="optionsTooltip || optionWithPreview || optionWithPreviewOnly"
69
+ slot="option"
70
+ slot-scope="{ option }"
71
+ >
72
+ <mc-title
73
+ v-if="(optionWithPreview || optionWithPreviewOnly) && option.$isLabel"
74
+ weight="semi-bold"
75
+ variation="subtitle"
76
+ >
77
+ {{ option.$groupLabel }}
78
+ </mc-title>
79
+ <mc-preview v-else-if="optionWithPreview || optionWithPreviewOnly" class="option__desc" size="l">
80
+ <template v-if="option?.icon || option?.image" #left>
81
+ <mc-avatar v-if="option.image" :src="option.image" size="400" />
82
+ <mc-svg-icon v-else :name="option.icon" :color="option.iconColor || 'main'" size="400" />
83
+ </template>
84
+ <mc-title slot="top" :weight="optionWithPreviewOnly ? '' : 'semi-bold'" v-html="option.name" />
85
+ <mc-title v-if="!!option.text" slot="bottom" color="dark-gray" :pre-line="option.preLine"
86
+ >{{ option.text }}
87
+ </mc-title>
88
+ </mc-preview>
89
+ <mc-tooltip
90
+ v-else
91
+ class="mc-field-select__options-tooltip-target"
92
+ max-width="m"
93
+ color="black"
94
+ placement="top"
95
+ :content="option.name"
96
+ >
97
+ <span>{{ option.name }}</span>
98
+ </mc-tooltip>
99
+ </template>
100
+ <!-- @slot Слот для текста, если ничего не найдено -->
101
+ <slot slot="noResult" name="noResult">
102
+ <span>{{ noResultsText }}</span>
103
+ </slot>
104
+ </multi-select>
105
+ </div>
106
+ <div v-if="errorText || helpText || $slots.footer" class="mc-field-select__footer">
107
+ <mc-title
108
+ v-if="errorText"
109
+ tag-name="div"
110
+ color="red"
111
+ variation="overline"
112
+ max-width="100%"
113
+ :ellipsis="false"
114
+ >
115
+ {{ errorText }}
116
+ </mc-title>
117
+ <br v-if="errorText" />
118
+ <!-- @slot Слот доп. текста под инпутом -->
119
+ <slot name="footer">
120
+ <mc-title
121
+ v-if="helpText"
122
+ tag-name="div"
123
+ variation="overline"
124
+ color="gray"
125
+ max-width="100%"
126
+ :ellipsis="false"
127
+ >
128
+ {{ helpText }}
129
+ </mc-title>
130
+ </slot>
131
+ </div>
132
+ </div>
133
+ </template>
134
+
135
+ <script>
136
+ import MultiSelect from 'vue-multiselect'
137
+ import McTitle from '../../McTitle/McTitle'
138
+ import McChip from '../../McChip/McChip'
139
+ import McTooltip from '../../McTooltip/McTooltip'
140
+ import McAvatar from '../../McAvatar/McAvatar'
141
+ import McSvgIcon from '../../McSvgIcon/McSvgIcon'
142
+ import McPreview from '../../../patterns/McPreview/McPreview'
143
+ import fieldErrors from '../../../mixins/fieldErrors'
144
+ import equalFieldHeight from '../../../mixins/equalFieldHeight'
145
+ import { LANGUAGES } from '../../../helpers/consts'
146
+
147
+ export default {
148
+ name: 'McFieldSelect',
149
+ components: { McSvgIcon, McAvatar, McTitle, McTooltip, MultiSelect, McPreview, McChip },
150
+ mixins: [fieldErrors, equalFieldHeight],
151
+ props: {
152
+ /**
153
+ * Заголовок поля:
154
+ *
155
+ */
156
+ title: {
157
+ type: String,
158
+ default: null,
159
+ },
160
+
161
+ /**
162
+ * Вспомогательный текст под инпутом:
163
+ *
164
+ */
165
+ helpText: {
166
+ type: String,
167
+ default: null,
168
+ },
169
+ /**
170
+ * Массив элементов
171
+ * выпадающего списка
172
+ * [
173
+ * {
174
+ * name: String,
175
+ * value: String | Number,
176
+ * text: String - доступен, если optionWithPreview=true
177
+ * icon: String - доступен, если optionWithPreview=true
178
+ * }
179
+ * ]
180
+ */
181
+ options: {
182
+ type: Array,
183
+ required: true,
184
+ },
185
+ /**
186
+ * Выполняется ли поиск из списка
187
+ * при вводе в инпут
188
+ */
189
+ searchable: {
190
+ type: Boolean,
191
+ default: true,
192
+ },
193
+ /**
194
+ * Множественный выбор
195
+ */
196
+ multiple: {
197
+ type: Boolean,
198
+ default: false,
199
+ },
200
+ /**
201
+ * Скрывать из списка
202
+ * выбранные элементы
203
+ */
204
+ hideSelected: {
205
+ type: Boolean,
206
+ default: true,
207
+ },
208
+ /**
209
+ * Допустимо ли
210
+ * пустое значение
211
+ */
212
+ allowEmpty: {
213
+ type: Boolean,
214
+ default: false,
215
+ },
216
+ /**
217
+ * Отключенное состояние
218
+ */
219
+ disabled: {
220
+ type: Boolean,
221
+ default: false,
222
+ },
223
+ /**
224
+ * Ссылка на аватар/картинку
225
+ * в начале label
226
+ */
227
+ avatar: {
228
+ type: String,
229
+ default: null,
230
+ },
231
+ /**
232
+ * Имя иконки
233
+ * в начале label
234
+ */
235
+ icon: {
236
+ type: String,
237
+ default: null,
238
+ },
239
+ /**
240
+ * Цвет фона
241
+ */
242
+ backgroundColor: {
243
+ type: String,
244
+ default: null,
245
+ },
246
+ /**
247
+ * placeholder
248
+ */
249
+ placeholder: {
250
+ type: String,
251
+ default: '',
252
+ },
253
+ /**
254
+ * Направление открытия списка:
255
+ * `above (top), below (bottom), auto`
256
+ */
257
+ openDirection: {
258
+ type: String,
259
+ default: 'auto',
260
+ },
261
+
262
+ taggable: {
263
+ type: Boolean,
264
+ default: false,
265
+ },
266
+ /**
267
+ * Помечать в списке выбранные
268
+ * элементы
269
+ */
270
+ showLabels: {
271
+ type: Boolean,
272
+ default: false,
273
+ },
274
+
275
+ internalSearch: {
276
+ type: Boolean,
277
+ default: true,
278
+ },
279
+ /**
280
+ * Значение
281
+ */
282
+ value: {
283
+ default: null,
284
+ },
285
+ /**
286
+ * Ошибки
287
+ */
288
+ errors: {
289
+ type: Array,
290
+ default: null,
291
+ },
292
+ /**
293
+ * Name
294
+ */
295
+ name: {
296
+ type: String,
297
+ required: true,
298
+ },
299
+ /**
300
+ * Если нужен тултип
301
+ * над элементами списка
302
+ */
303
+ optionsTooltip: {
304
+ type: Boolean,
305
+ default: false,
306
+ },
307
+ /**
308
+ * Если режим taggable && searchValueInOptions то добавлять введенный тег в опции
309
+ */
310
+ searchValueInOptions: {
311
+ type: Boolean,
312
+ default: true,
313
+ },
314
+ /**
315
+ * Группировка { label: 'label text', values: Array of objects }
316
+ */
317
+ groupKeys: {
318
+ type: Object,
319
+ default: null,
320
+ },
321
+ required: {
322
+ type: Boolean,
323
+ default: false,
324
+ },
325
+ /**
326
+ * Если айтемам в селекте нужны превью с иконками и описанием
327
+ */
328
+ optionWithPreview: {
329
+ type: Boolean,
330
+ default: false,
331
+ },
332
+ /**
333
+ * Если айтемам в селекте нужны превью с иконками и описанием
334
+ * (только в выпадающем списке. Выбранное значение будет выводиться без описания)
335
+ */
336
+ optionWithPreviewOnly: {
337
+ type: Boolean,
338
+ default: false,
339
+ },
340
+ tabindex: {
341
+ type: [String, Number],
342
+ default: null,
343
+ },
344
+ /**
345
+ * Если нужно ограничить максимальную высоту блока с выбранными элементами
346
+ */
347
+ maxHeight: {
348
+ type: String,
349
+ default: null,
350
+ },
351
+ /**
352
+ * Рендерить ли выпадающий список абсолютно, что бы помещался в ограниченном пространстве
353
+ * */
354
+ renderAbsoluteList: {
355
+ type: Boolean,
356
+ default: false,
357
+ },
358
+ /**
359
+ * Для какого языка селект
360
+ */
361
+ locale: {
362
+ type: String,
363
+ default: null,
364
+ },
365
+ /**
366
+ * Текст для пустого селекта, когда неичего не найдено
367
+ */
368
+ noResultsText: {
369
+ type: String,
370
+ default: 'No results',
371
+ },
372
+ /**
373
+ * Показывать ли состояние лоадинга
374
+ */
375
+ loading: {
376
+ type: Boolean,
377
+ default: false,
378
+ },
379
+ /**
380
+ * Ограничить ли отображение выбранных опций
381
+ */
382
+ collapsed: {
383
+ type: Boolean,
384
+ default: false,
385
+ },
386
+ },
387
+ data() {
388
+ return {
389
+ searchValue: null,
390
+ key: `field_select_${Date.now()}`,
391
+ field_key: `field-${this.name}`,
392
+ closest_scroll_element: null,
393
+ scroll_resize_observer: null,
394
+ local_options: [],
395
+ custom_limit: 0,
396
+ is_show_all_options: false,
397
+ }
398
+ },
399
+ computed: {
400
+ tagBind() {
401
+ return {
402
+ label: 'name',
403
+ trackBy: 'value',
404
+ value: this._value,
405
+ loading: this.loading,
406
+ options: this.collapsed ? this.visibleOptions : this.computedOptions,
407
+ searchable: this.searchable,
408
+ showLabels: this.showLabels,
409
+ multiple: this.multiple,
410
+ hideSelected: this.hideSelected,
411
+ allowEmpty: this.allowEmpty,
412
+ openDirection: this.openDirection,
413
+ id: this.name,
414
+ taggable: this.taggable,
415
+ tagPlaceholder: '',
416
+ placeholder: this.placeholder,
417
+ disabled: this.disabled,
418
+ internalSearch: this.internalSearch,
419
+ tabindex: +this.tabindex,
420
+ ...(this.groupKeys ? { groupLabel: this.groupKeys.label } : {}),
421
+ ...(this.groupKeys ? { groupValues: this.groupKeys.values } : {}),
422
+ ...(this.collapsed ? { limit: this.is_show_all_options ? this.value?.length : this.custom_limit } : {}),
423
+ class: this.collapsed ? 'mc-field-select__limit' : '',
424
+ }
425
+ },
426
+ visibleOptions() {
427
+ return this.is_show_all_options ? this.computedOptions : this.computedOptions.slice(0, this.custom_limit)
428
+ },
429
+ computedCaretClass() {
430
+ return {
431
+ multiselect__select: true,
432
+ 'mc-field-select__limit-toggle': true,
433
+ 'mc-field-select__limit-toggle--close': this.is_show_all_options,
434
+ }
435
+ },
436
+ limitText() {
437
+ return `+${+this.value?.length - +this.custom_limit}`
438
+ },
439
+ isShowLimitToggle() {
440
+ return this.collapsed && this.value?.length > this.custom_limit
441
+ },
442
+ hasTitle() {
443
+ return !!this.title
444
+ },
445
+ /**
446
+ * Если режим taggable && searchValueInOptions то добавлять введенный тег в опции
447
+ * **/
448
+ computedOptions() {
449
+ let options = !this.groupKeys
450
+ ? [...this.options, ...this.local_options].filter(
451
+ (v, i, a) =>
452
+ a.findIndex(afi => {
453
+ if (typeof afi.value === 'object') {
454
+ return JSON.stringify(afi.value) === JSON.stringify(v.value)
455
+ }
456
+ return String(afi.value) === String(v.value)
457
+ }) === i,
458
+ )
459
+ : this.options
460
+ if (this.searchValueInOptions && this.taggable) {
461
+ const search = this.searchValue
462
+ return search && search.length ? [{ name: search, value: search }, ...options] : options
463
+ }
464
+ return options
465
+ },
466
+ rtl() {
467
+ return LANGUAGES.rtl.includes(this.locale)
468
+ },
469
+ dir() {
470
+ return this.rtl ? 'rtl' : null
471
+ },
472
+ classes() {
473
+ return {
474
+ 'mc-field-select': true,
475
+ 'mc-field-select--error': this.errorText,
476
+ 'mc-field-select--disabled': this.disabled,
477
+ [`mc-field-select--bg-${this.backgroundColor}`]: this.backgroundColor,
478
+ 'mc-field-select--is-empty-options-list': this.isEmptyOptions,
479
+ 'mc-field-select--with-preview': this.optionWithPreview,
480
+ 'mc-field-select--hide-arrow': !!this.$slots.arrow,
481
+ 'mc-field-select--grouped': this.groupKeys,
482
+ 'mc-field-select--max-height': this.maxHeight,
483
+ 'mc-field-select--empty': !this._value,
484
+ 'mc-field-select--rtl': this.rtl,
485
+ }
486
+ },
487
+ isEmptyOptions() {
488
+ let options = this.computedOptions
489
+ if (this.groupKeys) {
490
+ options = []
491
+ for (let option of this.computedOptions) {
492
+ options.push(...option[this.groupKeys.values])
493
+ }
494
+ }
495
+ return this.isEmptyOptionsList || this.loading || options?.length === this._value?.length
496
+ },
497
+ computedTitle() {
498
+ return `${this.title}${this.required ? ' *' : ''}`
499
+ },
500
+ styles() {
501
+ const darkColors = ['gray', 'dark-gray', 'black']
502
+ const lightColors = ['white']
503
+ let placeHolderColor
504
+ let borderColor = this.backgroundColor
505
+ let backgroundColor = this.backgroundColor
506
+ let labelColor
507
+ if (!this.backgroundColor || lightColors.includes(this.backgroundColor)) {
508
+ borderColor = 'main'
509
+ }
510
+ if (darkColors.includes(this.backgroundColor)) {
511
+ labelColor = 'white'
512
+ placeHolderColor = 'white'
513
+ borderColor = 'black'
514
+ }
515
+ if (this.disabled && !this.backgroundColor) {
516
+ backgroundColor = 'hover-gray'
517
+ }
518
+ return {
519
+ '--mc-field-select-max-height': this.maxHeight,
520
+ '--mc-field-select-color': backgroundColor && `var(--color-${backgroundColor})`,
521
+ '--mc-field-select-border-color': borderColor && `var(--color-${borderColor})`,
522
+ '--mc-field-select-label-color': labelColor && `var(--color-${labelColor})`,
523
+ '--mc-field-select-placeholder-color': placeHolderColor && `var(--color-${placeHolderColor})`,
524
+ }
525
+ },
526
+ _value() {
527
+ if (this.multiple) {
528
+ if (this.value === null) return []
529
+ let result = []
530
+ for (let value of this.value) {
531
+ const options = [
532
+ ...(this.groupKeys
533
+ ? this.options.map(o => o[this.groupKeys.values]).flat()
534
+ : this.computedOptions),
535
+ ]
536
+ let option = options.find(o => {
537
+ if (o.value?.hasOwnProperty('id') && o.value.id == value.id) {
538
+ return true
539
+ }
540
+ return o.value == value
541
+ })
542
+ if (option !== null) result.push(option)
543
+ }
544
+ return result
545
+ }
546
+ if (this.groupKeys) {
547
+ let ungruppedOptions = []
548
+ for (let option of this.options) {
549
+ ungruppedOptions.push(...option[this.groupKeys.values])
550
+ }
551
+ return ungruppedOptions.find(o => o.value == this.value)
552
+ }
553
+ return this.computedOptions.find(o => o.value == this.value)
554
+ },
555
+
556
+ isEmptyOptionsList() {
557
+ if ((this.hideSelected && !this.searchValue) || !this.options.length) {
558
+ if (this.multiple) {
559
+ if (this.groupKeys) return false
560
+ return this.options.length === this._value.length
561
+ } else {
562
+ return this._value && this.computedOptions.length === 1 && !this.searchValue
563
+ }
564
+ } else if (this.options.length === 0) return !this.options.length
565
+ return false
566
+ },
567
+ hasPrepend() {
568
+ return this.avatar || this.icon
569
+ },
570
+ },
571
+ watch: {
572
+ options: {
573
+ immediate: true,
574
+ handler(val) {
575
+ //Пушим все входящие опции в локальные опции
576
+ this.local_options.push(...val)
577
+ this.actualizeSavedOptions()
578
+ this.calcLimit()
579
+ },
580
+ },
581
+ value: {
582
+ deep: true,
583
+ immediate: true,
584
+ handler() {
585
+ this.actualizeSavedOptions()
586
+ this.calcLimit()
587
+ },
588
+ },
589
+ },
590
+ methods: {
591
+ actualizeSavedOptions() {
592
+ //Фильтруем локальные опции и оставляем только те, значения которых выбраны в селекте
593
+ this.local_options = this.local_options.filter(lo =>
594
+ this.value?.constructor === Array
595
+ ? this.value.map(v => String(v)).includes(String(lo.value))
596
+ : String(lo.value) === String(this.value),
597
+ )
598
+
599
+ //Делаем Юник, что бы опции не повторялись
600
+ this.local_options = this.local_options.filter(
601
+ (v, i, a) =>
602
+ a.findIndex(afi => {
603
+ if (typeof afi.value === 'object') {
604
+ return JSON.stringify(afi.value) === JSON.stringify(v.value)
605
+ }
606
+ return String(afi.value) === String(v.value)
607
+ }) === i,
608
+ )
609
+ },
610
+ handleOpen() {
611
+ if (!this.renderAbsoluteList) return
612
+ this.initScroll()
613
+ },
614
+ handleClose() {
615
+ this.closest_scroll_element?.removeEventListener('scroll', this.repositionDropDown)
616
+ this.scroll_resize_observer?.disconnect()
617
+ this.scroll_resize_observer = null
618
+ },
619
+ findClosestScrollElement(element) {
620
+ if (!element) return document.documentElement
621
+ const { overflow, overflowY } = getComputedStyle(element)
622
+ const scrollableVariants = ['auto', 'scroll']
623
+ return scrollableVariants.some(v => [overflow, overflowY].includes(v))
624
+ ? element
625
+ : this.findClosestScrollElement(element.parentNode)
626
+ },
627
+ initScroll() {
628
+ // looking for closest scroll elemen to track select list position dynamically
629
+ this.closest_scroll_element = this.findClosestScrollElement(this.$refs[this.field_key])
630
+ this.closest_scroll_element.addEventListener('scroll', this.repositionDropDown)
631
+ this.scroll_resize_observer = new ResizeObserver(this.repositionDropDown)
632
+ this.scroll_resize_observer.observe(this.closest_scroll_element)
633
+ },
634
+ repositionDropDown() {
635
+ const { top, bottom, height, width, left } = this.$el.getBoundingClientRect()
636
+ const scrollElementRect = this.closest_scroll_element.getBoundingClientRect()
637
+ const ref = this.$refs[this.key]
638
+ if (!ref) return
639
+ const ios_devices = ['iPhone', 'iPad']
640
+ // Добавляем к позиции отступ visualViewport?.offsetTop, который добавляет iOs при открытии вирутальной клавиатуры
641
+ const iosViewportIndent = ios_devices?.some(device => navigator?.platform?.includes(device))
642
+ ? window.visualViewport?.offsetTop || 0
643
+ : 0
644
+ // Высчитываем реальную позицию селекта относительно первого скроллящегося родителя
645
+ const actualTop = top - scrollElementRect.top
646
+ const actualBottom = bottom - scrollElementRect.bottom
647
+ // if field hides under scrolled element borders -> blur select to prevent overlap
648
+ if (actualTop >= -height && actualBottom < height) {
649
+ ref.$refs.list.style.width = `${width}px`
650
+ ref.$refs.list.style.position = 'fixed'
651
+ ref.$refs.list.style.left = `${left}px`
652
+ const title_height = document.querySelector('.mc-field-select__header').offsetHeight
653
+ const title_margin = 8
654
+ let openDirection = this.openDirection
655
+ if (openDirection === 'auto') openDirection = ref?.isAbove ? 'top' : 'bottom'
656
+ switch (openDirection) {
657
+ case 'top':
658
+ ref.$refs.list.style.top = `${top +
659
+ (this.hasTitle ? title_height + title_margin : 0) +
660
+ iosViewportIndent -
661
+ ref.$refs.list.getBoundingClientRect().height -
662
+ 8}px`
663
+ ref.$refs.list.style.bottom = 'auto'
664
+ break
665
+ case 'bottom':
666
+ ref.$refs.list.style.bottom = 'auto'
667
+ ref.$refs.list.style.top = `${top + iosViewportIndent + height}px`
668
+ break
669
+ }
670
+ // Для андроидов не прячем селект при оверлапе, так как там работает все криво
671
+ // при открытии из нижней позиции клавиатура его перекрывает и он сразу сам закрывается
672
+ } else if (!/Android/i.test(navigator.userAgent)) {
673
+ // прячем селект, если его не видно юзеру
674
+ return ref.deactivate()
675
+ }
676
+ },
677
+ handleChange(value) {
678
+ /**
679
+ * Истинное значение инпута
680
+ */
681
+ this.$emit('original-input', value)
682
+ if (value !== null) {
683
+ if (this.multiple) {
684
+ value = value.map(v => v.value)
685
+ } else {
686
+ value = value.value
687
+ }
688
+ }
689
+ this.emitInput(value)
690
+ },
691
+
692
+ handleTag(value) {
693
+ /**
694
+ * Событие по добавлению
695
+ * тега в инпут (по Enter)
696
+ * @property {string}
697
+ */
698
+ this.$emit('tag', value)
699
+ },
700
+
701
+ handleSearchChange(value) {
702
+ this.searchValue = value
703
+ /**
704
+ * Событие по вводу данных в инпут
705
+ * @property {string}
706
+ */
707
+ this.$emit('search-change', value)
708
+ this.renderAbsoluteList && this.$nextTick(() => this.repositionDropDown())
709
+ },
710
+
711
+ emitInput(value) {
712
+ this.toggleErrorVisible()
713
+ /**
714
+ * Событие инпута (выбранное значение)
715
+ * @property {array, number}
716
+ */
717
+ this.$emit('input', value)
718
+ },
719
+ /**
720
+ * Вычисляем custom_limit, которое ограничивает кол-во дочерних элементов внутри родительского, чтобы они не превышали его ширину
721
+ * */
722
+ calcLimit() {
723
+ if (!this.collapsed) return
724
+ this.$nextTick(() => {
725
+ this.custom_limit = Infinity
726
+ let child_width = 0
727
+ const parent = this.$refs[this.key]?.$refs?.tags?.firstChild
728
+ if (!this.value?.length) return
729
+ const limit_text_width = this.getLimitTextWidth() // Получаем ширину текста лимита
730
+ const total_width = +parent?.clientWidth - +limit_text_width
731
+ for (let i = 0; i < this.value?.length; i++) {
732
+ const children = parent?.children?.[i]
733
+ const elem_style = window.getComputedStyle(children)
734
+ child_width += children?.clientWidth + (parseInt(elem_style?.marginRight) || 0)
735
+ // считаем занимаемую дочерними элементами ширину, если превышает родительскую, то выходим из цикла и ставим лимит
736
+ if (+child_width > +total_width) {
737
+ this.custom_limit = i
738
+ break
739
+ }
740
+ }
741
+ })
742
+ },
743
+ getLimitTextWidth() {
744
+ const temp_limit_element = document.createElement('div')
745
+ temp_limit_element.style.visibility = 'hidden'
746
+ temp_limit_element.style.position = 'absolute'
747
+ temp_limit_element.innerText = `+${this.value?.length}` // Устанавливаем текст лимита
748
+ document.body.appendChild(temp_limit_element)
749
+ const limit_text_width = temp_limit_element.clientWidth
750
+ document.body.removeChild(temp_limit_element)
751
+ return limit_text_width
752
+ },
753
+ toggleOptions() {
754
+ this.is_show_all_options = !this.is_show_all_options
755
+ },
756
+ },
757
+ }
758
+ </script>
759
+
760
+ <style lang="scss">
761
+ @import 'vue-multiselect/dist/vue-multiselect.min';
762
+ @import '../../../styles/mixins';
763
+ @import '../../../tokens/durations';
764
+ @import '../../../tokens/border-radius';
765
+ @import '../../../tokens/font-families';
766
+ @import '../../../tokens/box-shadows';
767
+ @import '../../../tokens/colors';
768
+ @import '../../../tokens/font-sizes';
769
+ @import '../../../tokens/line-heights';
770
+ @import '../../../tokens/font-weights';
771
+ @import '../../../tokens/sizes';
772
+ @import '../../../tokens/spacings';
773
+
774
+ .mc-field-select {
775
+ $block-name: &;
776
+ --mc-field-select-color: initial;
777
+ --mc-field-select-label-color: #{$color-black};
778
+ --mc-field-select-border-color: initial;
779
+ --mc-field-select-max-height: initial;
780
+ --mc-field-select-placeholder-color: #{$color-gray};
781
+ @include custom-scroll($space-100);
782
+ font-family: $font-family-main;
783
+
784
+ &__header {
785
+ @include reset-text-indents();
786
+ display: block;
787
+ margin-bottom: $space-100;
788
+
789
+ &:empty {
790
+ display: none;
791
+ }
792
+ }
793
+
794
+ &__footer {
795
+ margin-top: $space-50;
796
+ line-height: $line-height-150;
797
+
798
+ &:empty {
799
+ display: none;
800
+ }
801
+ }
802
+
803
+ &__single-label {
804
+ @include reset-text-indents();
805
+ position: relative;
806
+ display: flex;
807
+ flex-wrap: nowrap;
808
+ align-items: center;
809
+ @include child-indent-right($space-50);
810
+ }
811
+
812
+ &__prepend {
813
+ position: absolute;
814
+ }
815
+
816
+ &__label-text {
817
+ @include ellipsis();
818
+ font-size: $font-size-200;
819
+ line-height: $line-height-200;
820
+ padding-inline-start: $space-50;
821
+ color: var(--mc-field-select-label-color);
822
+
823
+ &--indent-left {
824
+ margin-inline-start: $space-300;
825
+ }
826
+ }
827
+
828
+ .multiselect {
829
+ &__placeholder {
830
+ @include ellipsis();
831
+ color: var(--mc-field-select-placeholder-color);
832
+ font-size: $font-size-200;
833
+ line-height: $line-height-200;
834
+ margin-bottom: $space-150 - 1px;
835
+ padding-top: $space-150 - 1px;
836
+ padding-inline-start: $space-50;
837
+ width: 100%;
838
+ }
839
+
840
+ &__single {
841
+ padding-inline-start: 0;
842
+ margin-bottom: $space-150 - 1px;
843
+ margin-top: $space-150 - 1px;
844
+ background-color: transparent;
845
+ min-height: auto;
846
+
847
+ @include input-placeholder() {
848
+ color: $color-gray;
849
+ }
850
+ }
851
+ &__tag {
852
+ padding-left: 2px !important;
853
+ .mc-chip__button {
854
+ background-color: $color-white;
855
+ border-radius: $radius-circle;
856
+ opacity: 1;
857
+ .mc-svg-icon {
858
+ min-width: $space-250;
859
+ width: $space-250;
860
+ min-height: $space-250;
861
+ height: $space-250;
862
+ }
863
+ &:hover {
864
+ .mc-svg-icon {
865
+ fill: var(--color-red);
866
+ }
867
+ }
868
+ .mc-svg-icon {
869
+ fill: var(--color-main);
870
+ }
871
+ }
872
+ }
873
+ &__input {
874
+ padding-inline-start: $space-50;
875
+ margin-bottom: $space-150 - 2px;
876
+ padding-top: $space-150 - 1px;
877
+ font-size: $font-size-200;
878
+ line-height: $line-height-200;
879
+ min-height: auto;
880
+ background-color: $color-transparent;
881
+ @include input-placeholder() {
882
+ color: $color-gray;
883
+ }
884
+ }
885
+
886
+ &__select {
887
+ overflow: hidden;
888
+ height: $space-350;
889
+ width: $space-300;
890
+ inset-inline-end: $space-100;
891
+ top: 6px;
892
+ padding: 0;
893
+ z-index: 1;
894
+
895
+ &::before {
896
+ direction: ltr;
897
+ width: 0;
898
+ height: 0;
899
+ border-left: 5px solid transparent;
900
+ border-right: 5px solid transparent;
901
+ border-bottom: 5px solid transparent;
902
+ border-top: 5px solid var(--mc-field-select-label-color);
903
+ }
904
+ }
905
+
906
+ &__tags {
907
+ @include reset-text-indents();
908
+ position: relative;
909
+ border: 1px solid $color-outline-gray;
910
+ border-radius: $radius-100 !important;
911
+ padding: 0;
912
+ padding-inline: $space-100 $space-500;
913
+ overflow: hidden;
914
+ text-align: start;
915
+
916
+ &:hover {
917
+ border-color: var(--color-main);
918
+ }
919
+
920
+ &:before {
921
+ content: '';
922
+ position: absolute;
923
+ left: 0;
924
+ top: 0;
925
+ @include size(100%);
926
+ background-color: var(--mc-field-select-color);
927
+ opacity: 0.6;
928
+ }
929
+ }
930
+
931
+ &__tags-wrap {
932
+ width: 100%;
933
+ position: relative;
934
+ padding-top: 4px;
935
+ padding-bottom: 3px;
936
+ top: 0;
937
+ display: flex;
938
+ flex-wrap: wrap;
939
+ margin-top: -1px;
940
+ min-height: $size-500 - 2px;
941
+ @include child-indent-right($space-100);
942
+ }
943
+
944
+ &__tag {
945
+ display: inline-flex;
946
+ align-items: center;
947
+ height: $size-300;
948
+ font-family: $font-family-main;
949
+ margin-top: $space-50;
950
+ margin-bottom: $space-50;
951
+ margin-right: unset;
952
+ background-color: var(--color-main-alpha-10);
953
+ color: $color-black;
954
+ padding: $size-50 $size-50 $size-50 $size-100;
955
+ border-radius: 100px;
956
+ font-size: $font-size-200;
957
+ line-height: $line-height-200;
958
+
959
+ span {
960
+ @include ellipsis();
961
+ flex: 1 1 auto;
962
+ //overflow: visible;
963
+ }
964
+ }
965
+
966
+ &__tag-icon {
967
+ @include size($size-200);
968
+ position: relative;
969
+ background-color: var(--color-main);
970
+ border-radius: $radius-circle;
971
+ flex: 0 0 auto;
972
+ margin-inline-start: $space-100;
973
+
974
+ &:hover {
975
+ background-color: $color-red;
976
+ }
977
+
978
+ &::after {
979
+ @include align(true, true, absolute);
980
+ top: 45%;
981
+ color: $color-white;
982
+ }
983
+ }
984
+
985
+ &__content {
986
+ padding: $size-100;
987
+ max-width: 100%;
988
+ font-size: $font-size-200;
989
+ line-height: $line-height-200;
990
+ }
991
+
992
+ &__content-wrapper {
993
+ top: calc(100% + #{$size-100});
994
+ border: none;
995
+ border-radius: $radius-150;
996
+ box-shadow: $shadow-s;
997
+ transition: opacity $duration-s ease;
998
+ overflow-y: auto;
999
+ overflow-x: hidden;
1000
+ max-height: 300px;
1001
+ }
1002
+
1003
+ &--above {
1004
+ .multiselect__content-wrapper {
1005
+ bottom: calc(100% + #{$size-100});
1006
+ top: auto;
1007
+ }
1008
+ }
1009
+
1010
+ &__option {
1011
+ min-height: $size-500;
1012
+ display: flex;
1013
+ align-items: center;
1014
+ border-radius: $radius-100;
1015
+ padding: $space-150;
1016
+
1017
+ span {
1018
+ @include ellipsis();
1019
+ }
1020
+
1021
+ &--highlight {
1022
+ background-color: $color-hover-gray;
1023
+ color: $color-black;
1024
+ }
1025
+
1026
+ &--selected {
1027
+ background-color: var(--color-main-alpha-10) !important;
1028
+ color: $color-black !important;
1029
+ font-weight: $font-weight-medium;
1030
+ }
1031
+
1032
+ &--group.multiselect__option--disabled {
1033
+ background-color: $color-white !important;
1034
+ }
1035
+
1036
+ .option__desc {
1037
+ .mc-preview__bottom {
1038
+ margin-top: 0;
1039
+ }
1040
+ }
1041
+ }
1042
+
1043
+ &--active {
1044
+ .multiselect {
1045
+ &__tags {
1046
+ &:before {
1047
+ background-color: $color-transparent;
1048
+ }
1049
+
1050
+ border-color: var(--mc-field-select-border-color);
1051
+ }
1052
+
1053
+ &__select {
1054
+ &::before {
1055
+ border-color: var(--color-main) $color-transparent $color-transparent;
1056
+ }
1057
+ }
1058
+ }
1059
+ }
1060
+
1061
+ &__spinner {
1062
+ &:after,
1063
+ &:before {
1064
+ border-top-color: var(--color-main);
1065
+ @include size($space-300);
1066
+ top: calc(50% - 5px);
1067
+ left: calc(50% - 2px);
1068
+ }
1069
+ }
1070
+ }
1071
+
1072
+ &--error {
1073
+ .multiselect {
1074
+ &__tags {
1075
+ border-color: $color-red !important;
1076
+ }
1077
+ }
1078
+ }
1079
+
1080
+ &--hide-arrow {
1081
+ .multiselect {
1082
+ &__select {
1083
+ display: none !important;
1084
+ }
1085
+ }
1086
+ }
1087
+
1088
+ &--is-empty-options-list {
1089
+ .multiselect {
1090
+ &__content-wrapper {
1091
+ display: none !important;
1092
+ }
1093
+ }
1094
+ }
1095
+
1096
+ &--disabled {
1097
+ cursor: not-allowed;
1098
+
1099
+ .multiselect--disabled {
1100
+ opacity: 1;
1101
+ background: transparent;
1102
+
1103
+ .multiselect {
1104
+ &__placeholder {
1105
+ color: $color-gray;
1106
+ }
1107
+
1108
+ &__single {
1109
+ & #{$block-name}__label-text {
1110
+ color: $color-gray;
1111
+ }
1112
+ }
1113
+
1114
+ &__select {
1115
+ background-color: transparent;
1116
+
1117
+ &::before {
1118
+ border-color: $color-outline-gray transparent transparent;
1119
+ }
1120
+ }
1121
+ }
1122
+ }
1123
+ }
1124
+
1125
+ &--with-preview {
1126
+ .mc-preview {
1127
+ align-items: center;
1128
+ }
1129
+
1130
+ &#{$block-name} {
1131
+ &--grouped {
1132
+ .multiselect {
1133
+ &__content {
1134
+ padding: $space-200;
1135
+ }
1136
+ }
1137
+ }
1138
+
1139
+ &--empty {
1140
+ .multiselect {
1141
+ &__tags {
1142
+ padding: 0;
1143
+ border-radius: $radius-100 !important;
1144
+ }
1145
+
1146
+ &__placeholder {
1147
+ padding-inline-start: $space-150;
1148
+ }
1149
+
1150
+ &__select {
1151
+ display: block;
1152
+ }
1153
+ }
1154
+ }
1155
+ }
1156
+
1157
+ .multiselect {
1158
+ &__content {
1159
+ padding: 0;
1160
+ }
1161
+
1162
+ &--active {
1163
+ .multiselect__select {
1164
+ transform: translateY(-50%) rotate(180deg);
1165
+ }
1166
+ }
1167
+
1168
+ &__select {
1169
+ top: 50%;
1170
+ transform: translateY(-50%);
1171
+ }
1172
+
1173
+ &__single {
1174
+ margin: 0;
1175
+ }
1176
+
1177
+ &__tags {
1178
+ padding: $space-200 0;
1179
+ padding-inline: $space-150;
1180
+ cursor: pointer;
1181
+ border-color: $color-outline-light;
1182
+ }
1183
+
1184
+ &__option,
1185
+ &__content-wrapper,
1186
+ &__tags {
1187
+ border-radius: $radius-200 !important;
1188
+ }
1189
+
1190
+ &__option {
1191
+ padding: $space-200 $space-150;
1192
+
1193
+ &--group {
1194
+ padding: 0 0 $space-100 0;
1195
+ min-height: auto;
1196
+ }
1197
+ }
1198
+
1199
+ &__element {
1200
+ &:not([role='option']) {
1201
+ &:not(:first-of-type) {
1202
+ margin-top: $space-300;
1203
+ }
1204
+ }
1205
+ }
1206
+ }
1207
+ }
1208
+
1209
+ &--max-height {
1210
+ .multiselect {
1211
+ &__tags {
1212
+ max-height: var(--mc-field-select-max-height);
1213
+ overflow-y: auto;
1214
+ position: initial;
1215
+ }
1216
+
1217
+ &__spinner {
1218
+ background: transparent;
1219
+ right: calc(#{$space-50} / 2);
1220
+ top: calc(#{$space-50} / 2);
1221
+ }
1222
+ }
1223
+ }
1224
+
1225
+ &__options-tooltip-target {
1226
+ overflow: hidden;
1227
+ text-overflow: ellipsis;
1228
+ white-space: nowrap;
1229
+ }
1230
+
1231
+ &--rtl {
1232
+ direction: rtl;
1233
+ }
1234
+
1235
+ &__limit {
1236
+ &-text {
1237
+ position: relative;
1238
+ z-index: 1;
1239
+ width: auto;
1240
+ }
1241
+
1242
+ &-toggle {
1243
+ pointer-events: auto;
1244
+
1245
+ &:before {
1246
+ border-color: black transparent transparent !important;
1247
+ }
1248
+
1249
+ &--close {
1250
+ transform: rotate(180deg);
1251
+ }
1252
+ }
1253
+
1254
+ .multiselect__tags {
1255
+ display: flex;
1256
+ justify-content: space-between;
1257
+ align-items: center;
1258
+ }
1259
+ }
1260
+ }
1261
+ </style>