# ZeenSelect
Компонент select для форм
# Примеры:
Select:
Disabled
Select с ошибкой:
Select с текстом, что все верно:
Select без возможности ввода текста:
Select с кастомными цветами:
# props
Название | Тип | Обязательный | По умолчанию | Описание |
---|---|---|---|---|
value | object или null | + | { name: '', label: ''} | то, что указано в data родителя |
placeholder | string | - | - | |
disabled | boolean | - | - | если true, компонент становится disabled |
error | string | - | '' | если значение truthy, то отобразится переданный текст и сменится цвет лейбла и бордера |
correct | string | - | '' | если значение truthy, то отобразится переданный текст и сменится цвет лейбла и бордера |
color | object | - | {} | объект с кастомными цветами (подробнее см.ниже) |
options | array | + | [] | массив опция. каждая опция имеет label и value |
searchable | boolean | - | true | если true, то можно осуществить поиск по опциям |
Благодаря v-bind="$attrs" поддерживаются дополнительные атрибуты.
# Кастомные цвета в объекте color
Обработка части цветов осуществляется миксином InputColorMixin
Название | Тип | Обязательный | По умолчанию |
---|---|---|---|
textColor | цвет текста инпута | - | берет значение из темы ($input-text-color) |
backgroundColor | цвет фона инпута | - | берет значение из темы ($input-background-color) |
errorColor | цвет лейбла и бордера при truthy error | - | берет значение из темы ($input-error-color) |
correctColor | цвет лейбла и бордера при truthy correct | - | берет значение из темы ($input-correct-color) |
optionsBackgroundColor | цвет фона опций | - | берет значение из темы ($select-options-background-color) |
optionSelectedBackgroundColor | цвет фона выбранной опции или опции с ховером | - | берет значение из темы ($select-option-selected-background-color) |
optionSelectedColor | цвет текста выбранной опции | - | берет значение из темы ($select-option-selected-color) |
optionColor | цвет текста опций | - | берет значение из темы ($select-options-color) |
# Source Code - исходный код компонента
<template>
<div
:id="`zeen-select-${componentId}`"
class="zeen-select"
:class="{
'zeen-select_error': error,
'zeen-select_correct': correct,
}"
>
<label v-if="error || correct" class="zeen-select__label" :for="componentId">{{ error || correct }}</label>
<vSelect
ref="select"
:id="componentId"
:options="options"
:value="value"
:placeholder="placeholder"
:clearable="false"
:searchable="searchable"
@input="onSelect"
@open="notSearchable"
@close="closeSelect"
v-bind="$attrs"
:disabled="disabled"
class="zeen-select__select"
>
<template v-slot:open-indicator="{attributes}">
<span class="zeen-select__arrow" v-bind="attributes">
<svg width="12" height="7" viewBox="0 0 12 7" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1L6 6L11 1" stroke="#908494" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</span>
</template>
<template v-slot:no-options="{}">
<span>{{ notFoundText }}</span>
</template>
<template v-slot:selected-option-container="{option}">
<div class="vs__selected">{{ option.label }}</div>
</template>
</vSelect>
</div>
</template>
<script>
import vSelect from 'vue-select'
export default {
name: 'ZeenSelect',
data() {
return {
isOpen: '',
}
},
props: {
value: {
validator: (prop) => prop === null || typeof prop === 'object',
required: true,
default: () => {
return {
name: '',
label: '',
}
},
},
options: {
type: Array,
required: true,
default() {
return []
},
},
searchable: {
type: Boolean,
default: true,
},
placeholder: {
type: String,
default: 'Выберите опцию...',
},
disabled: {
type: Boolean,
},
error: {
type: String,
default: '',
},
correct: {
type: String,
default: '',
},
notFoundText: {
type: String,
default: 'Ничего не найдено',
},
},
components: {
vSelect,
},
methods: {
onSelect(val) {
this.$emit('input', val)
},
notSearchable() {
this.isOpen = true
const searchStyle = this.$refs.select.$refs.search.style
if (!this.searchable && this.$refs.select.$refs.selectedOptions.children.length > 1) {
searchStyle.zIndex = '-1'
} else if (!this.searchable) {
searchStyle.pointerEvents = 'none'
}
if (this.isOpen) {
this.$refs.select.$refs.selectedOptions.children[0].textContent = this.placeholder
}
},
closeSelect() {
this.isOpen = false
if (!this.isOpen && this.value) {
return (this.$refs.select.$refs.selectedOptions.children[0].textContent = this.value.label)
}
},
},
computed: {
componentId() {
return this._uid
},
},
}
</script>
<style lang="scss">
@import '~vue-select/src/scss/vue-select.scss';
:root {
/* Размеры */
--select-label-size: var(--checkbox-size);
--select-border-radius: var(--main-input-radius);
--select-border-width: var(--main-input-border-width);
--select-drop-max-width: 268px;
--select-text-weight: 400;
--select-opened-text-weight: var(--select-text-weight);
--select-opened-text-opacity: 1;
--select-search-padding: 15px 25px;
--select-search-font-size: 16px;
--select-search-border-radius: 10px;
/* Цвета */
--select-background-color: var(--input-background-color);
--select-text-color: var(--input-main-value-color);
--select-label-color: var(--select-text-color);
--select-error-color: var(--input-error-color);
--select-correct-color: var(--input-correct-color);
--select-placeholder-color: var(--input-main-placeholder-color);
--select-placeholder-disabled-color: var(--input-main-placeholder-color);
--select-option-selected-color: var(--main-light);
--select-option-color: var(--input-main-value-color);
--select-options-background-color: var(--main-disable-color);
--select-option-selected-background-color: var(--main-color);
--select-opened-text-color: var(--input-main-placeholder-color);
--select-arrow-color: var(--input-main-value-color);
--select-border-color: transparent;
--select-label-error-color: var(--select-error-color);
--select-border-error-color: var(--select-error-color);
--select-label-correct-color: var(--select-correct-color);
--select-border-correct-color: var(--select-correct-color);
}
</style>
<style lang="scss" scoped>
.zeen-select {
&__label {
display: block;
font-size: var(--select-label-size);
line-height: 1.4;
color: var(--select-label-color);
margin: 0 0 var(--main-input-label-offset-bottom) var(--main-input-label-offset-left);
}
&__arrow {
width: 12px;
height: 7px;
display: flex;
& path {
stroke: var(--select-arrow-color);
}
}
&__select {
border: var(--select-border-width) solid var(--select-border-color);
border-radius: var(--select-border-radius);
}
::v-deep .vs__search {
width: 100%;
box-sizing: border-box;
border-radius: var(--select-search-border-radius);
padding: var(--select-search-padding);
font-size: var(--select-search-font-size);
line-height: 1.4;
-webkit-appearance: none;
outline: none;
margin: 0;
&::placeholder {
font-size: inherit;
line-height: inherit;
color: var(--select-placeholder-color);
}
&:disabled {
--select-placeholder-color: var(--select-placeholder-disabled-color);
}
&:focus,
&:active {
margin: 0;
}
}
::v-deep .vs__dropdown-toggle {
padding: 0;
border-radius: var(--select-border-radius);
border: 0;
transition: background 0.25s ease;
}
::v-deep .vs--open .vs__dropdown-toggle {
border-radius: var(--select-border-radius) var(--select-border-radius) 0 0;
}
::v-deep .vs__selected-options {
padding: 0;
min-width: 1px;
flex-wrap: nowrap;
white-space: nowrap;
overflow: hidden;
border-radius: var(--select-border-radius);
}
::v-deep .vs__selected {
background: transparent;
margin: 0;
padding: 15px 20px 15px 25px;
border-radius: var(--select-border-radius);
display: flex;
align-items: center;
+ .vs__selected {
margin-left: -2px;
&::before {
display: inline-block;
content: ',';
position: absolute;
left: -13px;
top: 13px;
}
}
+ .vs__search {
margin-left: -2px;
}
}
::v-deep .vs__actions {
padding: 0 24px 0 24px;
border-radius: var(--select-border-radius);
}
::v-deep .vs__dropdown-menu {
top: calc(100% - 4px);
padding: 0;
box-shadow: none;
border: 0 !important;
border-radius: 0 0 var(--select-border-radius) var(--select-border-radius);
max-height: var(--select-drop-max-width) !important;
background: var(--select-options-background-color);
}
::v-deep .vs__dropdown-option {
position: relative;
padding: 8px 30px;
font-size: 16px;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transition: background 0.1s ease, color 0.25s ease;
color: var(--select-option-color);
background: var(--select-options-background-color);
}
::v-deep .vs__dropdown-option--selected {
padding-right: 45px;
&::after {
display: inline-block;
content: ' ';
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
width: 9px;
height: 7px;
}
}
::v-deep .vs__no-options {
padding: 14px 20px;
font-size: 16px;
line-height: 24px;
text-align: center;
}
&_error {
--select-label-color: var(--select-label-error-color);
--select-border-color: var(--select-border-error-color);
}
&_correct {
--select-label-color: var(--select-label-correct-color);
--select-border-color: var(--select-border-correct-color);
}
::v-deep {
.vs--open .vs__search,
.vs__search,
.vs__selected {
color: var(--select-text-color);
font-weight: var(--select-text-weight);
}
.vs__search,
.vs__dropdown-toggle {
background: var(--select-background-color);
}
.vs__dropdown-option--highlight {
color: var(--select-option-selected-color);
background: var(--select-option-selected-background-color);
}
.vs--open .vs__selected {
color: var(--select-opened-text-color);
font-weight: var(--select-opened-text-weight);
opacity: var(--select-opened-text-opacity);
}
}
}
</style>
← ZeenRadio ZeenSwitch →