Newer
Older

Дмитрий Малюгин
committed
<script setup lang="ts">
import type { ISelectProps } from '@interfaces/componentsProps';
import { computed, ref } from 'vue';
import { convertThemeToColor } from '@helpers/common';
import { iconsSet } from '@/common/constants/icons';
import type { TThemeColor } from '@interfaces/common';
import SelectItem from '@stories/components/Select/SelectItem.vue';

Дмитрий Малюгин
committed
const props = withDefaults(defineProps<ISelectProps>(), {
size: 'normal',
width: '200px',
theme: 'black',
darknessTheme: '700',
darknessBackground: '200',
darknessOpenIcon: '700',

Дмитрий Малюгин
committed
name: 'select',
placeholder: 'Nothing selected',
openIcon: 'ArrowShortDown',
});
const selected = defineModel('value');
const isOpen = ref<boolean>(false);
const selectedOption = computed(() => props.options.find((option) => option.value === selected.value));
const textColor = computed(() => (props.disabled ? '#62708c' : convertThemeToColor(props.theme, props.darknessTheme)));

Дмитрий Малюгин
committed
const backgroundColor = computed(() =>
convertThemeToColor(
props.background ?? (props.theme === 'white' ? 'black' : props.theme === 'black' ? 'white' : props.theme),
(!props.background && props.theme === 'black') || props.background === 'white' ? '500' : props.darknessBackground,

Дмитрий Малюгин
committed
),
);
const fontSize = computed(() => {
const size = props.size;
if (size === 'normal') return '16px';
if (size === 'large') return '20px';
if (size === 'huge') return '24px';
return '12px';
});
const padding = computed(() => {
const size = props.size;
if (size === 'normal') return '6px';
if (size === 'large') return '10px';
if (size === 'huge') return '14px';
return '4px';
});

Дмитрий Малюгин
committed
const pickOption = (value: string) => {
selected.value = value;
isOpen.value = false;
};
const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string | undefined, defaultColor: string) =>
color ? convertThemeToColor(color, darknessColor ?? '500') : defaultColor;
</script>
<template>
<section>
<select :name="name" id="select">
<option value=""></option>
<option v-for="option of options" :key="option.value" :selected="selected === option.value">
{{ option.label ?? option.value }}
</option>
</select>
<div class="list" :style="`background-color: ${backgroundColor}`">
<button
@click.prevent="!disabled ? (isOpen = !isOpen) : ''"
:class="[
'button',
{
disabled: disabled,
},
]"
:style="`width: ${width}`"
>
<SelectItem

Дмитрий Малюгин
committed
class="selected"
:style="`color: ${selected ? calcOptionColor(selectedOption?.color, selectedOption?.darknessColor, textColor) : placeholderColor ? convertThemeToColor(placeholderColor, '700') : '#62708c'}; font-weight: 600`"
:option="selectedOption"
:fontSize="fontSize"
:textColor="textColor"

Дмитрий Малюгин
committed
>
<slot :name="`icon-left-${selectedOption?.value}`"></slot>
<span :style="`font-size: ${fontSize}`">{{ selected ?? placeholder }}</span>
<slot :name="`icon-right-${selectedOption?.value}`"></slot>
</SelectItem>
<component

Дмитрий Малюгин
committed
:is="iconsSet[openIcon]"
:size="fontSize.slice(0, -2)"
:color="openIconColor ? convertThemeToColor(openIconColor, darknessOpenIcon) : '#62708c'"

Дмитрий Малюгин
committed
:style="`width: ${fontSize}`"
/>
</button>
<div
:class="[
'options',
{
optionsOpened: isOpen,
},
]"
>
<div style="overflow: hidden">
<SelectItem

Дмитрий Малюгин
committed
@click.prevent="pickOption(option.value)"
v-for="option of options"
:key="option.value"
:class="[
'option',
{
firstOption: options[0].value === option.value,
lastOption: options[options.length - 1].value === option.value,
},
]"
:style="`color: ${calcOptionColor(option.color, option.darknessColor, textColor)};
background-color: ${calcOptionColor(option.background, option.darknessBackground, backgroundColor)}`"
:option="option"
:fontSize="fontSize"
:textColor="textColor"

Дмитрий Малюгин
committed
>
<slot :name="`icon-left-${option.value}`"></slot>
<span :style="`font-size: ${fontSize}`">{{ option.label ?? option.value }}</span>

Дмитрий Малюгин
committed
<slot :name="`icon-right-${option.value}`"></slot>
</SelectItem>

Дмитрий Малюгин
committed
</div>
</div>
</div>
</section>
</template>
<style scoped>
#select {
display: none;
}
.list {
position: relative;
width: max-content;
border: 1px solid v-bind(textColor);
border-radius: 5px;
cursor: pointer;
}
.button {
display: flex;
padding: v-bind(padding);

Дмитрий Малюгин
committed
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
justify-content: space-between;
gap: 10px;
}
.selected {
display: flex;
gap: 5px;
}
.options {
position: absolute;
z-index: 5000;
top: 101%;
width: 100%;
border: 1px solid v-bind(textColor);
border-radius: 5px;
display: grid;
grid-template-rows: 0fr;
opacity: 0;
transition:
all 0.2s ease-in-out,
opacity 0.1s ease-in-out;
}
.optionsOpened {
grid-template-rows: 1fr;
opacity: 1;
}
.option {
display: flex;
align-items: center;
gap: 5px;
padding: v-bind(padding);

Дмитрий Малюгин
committed
}
.option:hover {
filter: brightness(90%);
transition: all 0.1s ease-in-out;
}
.group {
border-top: 1px solid v-bind(textColor);
}
.firstOption {
border-top-right-radius: 4px;
border-top-left-radius: 4px;
}
.lastOption {
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
}
.disabled {
cursor: auto;
background-color: #e1e7f1 !important;
border-radius: 4px;
}