mediacube-ui 0.1.346 → 0.1.347

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