Skip to content
Snippets Groups Projects
Commit 599e574a authored by Дмитрий Малюгин's avatar Дмитрий Малюгин :clock4:
Browse files

feat: add logic for Select and create 'SelectItem' component

parent 69c55394
No related branches found
No related tags found
1 merge request!3Table (partially), Checkbox, Tag, Select and Knob
...@@ -58,9 +58,11 @@ export interface IMDItemProps { ...@@ -58,9 +58,11 @@ export interface IMDItemProps {
export interface ISelectOption { export interface ISelectOption {
value: string; value: string;
label?: string; label?: string;
group?: string;
iconLeft?: TIcons; iconLeft?: TIcons;
iconRight?: TIcons; iconRight?: TIcons;
iconColor?: TThemeColor; iconLeftColor?: TThemeColor;
iconRightColor?: TThemeColor;
color?: TThemeColor; color?: TThemeColor;
darknessColor?: TDarkness; darknessColor?: TDarkness;
background?: TThemeColor; background?: TThemeColor;
...@@ -68,10 +70,12 @@ export interface ISelectOption { ...@@ -68,10 +70,12 @@ export interface ISelectOption {
} }
export interface ISelectGroup { export interface ISelectGroup {
title: string; name: string;
titleColor?: TThemeColor;
iconLeft?: TIcons; iconLeft?: TIcons;
iconRight?: TIcons; iconRight?: TIcons;
items: string[]; iconLeftColor?: TThemeColor;
iconRightColor?: TThemeColor;
} }
export interface ISBOption { export interface ISBOption {
......
...@@ -134,8 +134,11 @@ export interface ISelectProps { ...@@ -134,8 +134,11 @@ export interface ISelectProps {
name?: string; name?: string;
theme?: TThemeColor; theme?: TThemeColor;
background?: TThemeColor; background?: TThemeColor;
placeholderColor?: TThemeColor;
openIconColor?: TThemeColor;
darknessTheme?: TDarkness; darknessTheme?: TDarkness;
darknessBackground?: TDarkness; darknessBackground?: TDarkness;
darknessOpenIcon?: TDarkness;
disabled?: boolean; disabled?: boolean;
} }
......
...@@ -102,7 +102,7 @@ export const Full: Story = { ...@@ -102,7 +102,7 @@ export const Full: Story = {
}, },
}; };
export const Down: Story = { export const Vertical: Story = {
args: { args: {
items: [ items: [
{ {
...@@ -125,7 +125,7 @@ export const Down: Story = { ...@@ -125,7 +125,7 @@ export const Down: Story = {
}, },
}; };
export const Huge: Story = { export const Full2: Story = {
args: { args: {
items: [ items: [
{ {
......
import type { Meta, StoryObj } from '@storybook/vue3'; import type { Meta, StoryObj } from '@storybook/vue3';
import Select from './Select.vue'; import Select from './Select.vue';
import { iconsSet } from '@/common/constants/icons';
const meta: Meta = { const meta: Meta = {
title: 'Components/Select', title: 'Components/Select',
...@@ -18,9 +19,11 @@ const meta: Meta = { ...@@ -18,9 +19,11 @@ const meta: Meta = {
disabled: { control: 'boolean' }, disabled: { control: 'boolean' },
placeholder: { control: 'text' }, placeholder: { control: 'text' },
name: { control: 'text' }, name: { control: 'text' },
openIcon: { control: 'select', options: Object.keys(iconsSet) },
size: { control: 'select', options: ['small', 'normal', 'large', 'huge'] }, size: { control: 'select', options: ['small', 'normal', 'large', 'huge'] },
darknessTheme: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, darknessTheme: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] },
darknessBackground: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, darknessBackground: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] },
darknessOpenIcon: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] },
theme: { theme: {
control: 'select', control: 'select',
options: [ options: [
...@@ -61,6 +64,46 @@ const meta: Meta = { ...@@ -61,6 +64,46 @@ const meta: Meta = {
'black', 'black',
], ],
}, },
openIconColor: {
control: 'select',
options: [
'white',
'blue',
'sky',
'cyan',
'teal',
'green',
'yellow',
'orange',
'pink',
'fuchsia',
'purple',
'indigo',
'rose',
'red',
'black',
],
},
placeholderColor: {
control: 'select',
options: [
'white',
'blue',
'sky',
'cyan',
'teal',
'green',
'yellow',
'orange',
'pink',
'fuchsia',
'purple',
'indigo',
'rose',
'red',
'black',
],
},
}, },
} satisfies Meta<typeof Select>; } satisfies Meta<typeof Select>;
...@@ -95,7 +138,7 @@ export const Full: Story = { ...@@ -95,7 +138,7 @@ export const Full: Story = {
}, },
{ {
value: 'Second', value: 'Second',
iconColor: 'red', iconRightColor: 'red',
iconRight: 'Age18', iconRight: 'Age18',
}, },
{ {
...@@ -112,5 +155,6 @@ export const Full: Story = { ...@@ -112,5 +155,6 @@ export const Full: Story = {
background: 'sky', background: 'sky',
darknessTheme: '700', darknessTheme: '700',
darknessBackground: '200', darknessBackground: '200',
openIconColor: 'sky',
}, },
}; };
...@@ -4,6 +4,7 @@ import { computed, ref } from 'vue'; ...@@ -4,6 +4,7 @@ import { computed, ref } from 'vue';
import { convertThemeToColor } from '@helpers/common'; import { convertThemeToColor } from '@helpers/common';
import { iconsSet } from '@/common/constants/icons'; import { iconsSet } from '@/common/constants/icons';
import type { TThemeColor } from '@interfaces/common'; import type { TThemeColor } from '@interfaces/common';
import SelectItem from '@stories/components/Select/SelectItem.vue';
const props = withDefaults(defineProps<ISelectProps>(), { const props = withDefaults(defineProps<ISelectProps>(), {
size: 'normal', size: 'normal',
...@@ -11,22 +12,20 @@ const props = withDefaults(defineProps<ISelectProps>(), { ...@@ -11,22 +12,20 @@ const props = withDefaults(defineProps<ISelectProps>(), {
theme: 'black', theme: 'black',
darknessTheme: '700', darknessTheme: '700',
darknessBackground: '200', darknessBackground: '200',
darknessOpenIcon: '700',
name: 'select', name: 'select',
placeholder: 'Nothing selected', placeholder: 'Nothing selected',
openIcon: 'ArrowShortDown', openIcon: 'ArrowShortDown',
}); });
const selected = defineModel('value'); const selected = defineModel('value');
// watch(, () => {});
const isOpen = ref<boolean>(false); const isOpen = ref<boolean>(false);
const selectedOption = computed(() => props.options.find((option) => option.value === selected.value)); const selectedOption = computed(() => props.options.find((option) => option.value === selected.value));
const textColor = computed(() => convertThemeToColor(props.theme, props.darknessTheme)); const textColor = computed(() => (props.disabled ? '#62708c' : convertThemeToColor(props.theme, props.darknessTheme)));
const backgroundColor = computed(() => const backgroundColor = computed(() =>
convertThemeToColor( convertThemeToColor(
props.background ?? (props.theme === 'white' ? 'black' : props.theme === 'black' ? 'white' : props.theme), props.background ?? (props.theme === 'white' ? 'black' : props.theme === 'black' ? 'white' : props.theme),
(!props.background && props.theme === 'black') || (props.background === 'white' && props.theme === 'black') (!props.background && props.theme === 'black') || props.background === 'white' ? '500' : props.darknessBackground,
? '500'
: props.darknessBackground,
), ),
); );
const fontSize = computed(() => { const fontSize = computed(() => {
...@@ -36,6 +35,13 @@ const fontSize = computed(() => { ...@@ -36,6 +35,13 @@ const fontSize = computed(() => {
if (size === 'huge') return '24px'; if (size === 'huge') return '24px';
return '12px'; 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';
});
const pickOption = (value: string) => { const pickOption = (value: string) => {
selected.value = value; selected.value = value;
...@@ -54,38 +60,31 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string | ...@@ -54,38 +60,31 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string |
</option> </option>
</select> </select>
<div class="list" :style="`background-color: ${backgroundColor}`"> <div class="list" :style="`background-color: ${backgroundColor}`">
<button @click.prevent="isOpen = !isOpen" class="button" :style="`width: ${width}`"> <button
<span @click.prevent="!disabled ? (isOpen = !isOpen) : ''"
:class="[
'button',
{
disabled: disabled,
},
]"
:style="`width: ${width}`"
>
<SelectItem
class="selected" class="selected"
:style="`color: ${selected ? calcOptionColor(selectedOption?.color, selectedOption?.darknessColor, textColor) : '#62708c'}; font-weight: 600`" :style="`color: ${selected ? calcOptionColor(selectedOption?.color, selectedOption?.darknessColor, textColor) : placeholderColor ? convertThemeToColor(placeholderColor, '700') : '#62708c'}; font-weight: 600`"
:option="selectedOption"
:fontSize="fontSize"
:textColor="textColor"
> >
<slot :name="`icon-left-${selectedOption?.value}`"></slot> <slot :name="`icon-left-${selectedOption?.value}`"></slot>
<component <span :style="`font-size: ${fontSize}`">{{ selected ?? placeholder }}</span>
v-if="selectedOption?.iconLeft" <slot :name="`icon-right-${selectedOption?.value}`"></slot>
:is="iconsSet[selectedOption?.iconLeft]" </SelectItem>
:size="fontSize.slice(0, -2)" <component
:color="
calcOptionColor(
selectedOption.iconColor ?? selectedOption?.color,
selectedOption?.darknessColor,
textColor,
)
" /><span>{{ selected ?? placeholder }}</span>
<component
v-if="selectedOption?.iconRight"
:is="iconsSet[selectedOption?.iconRight]"
:size="fontSize.slice(0, -2)"
:color="
calcOptionColor(
selectedOption.iconColor ?? selectedOption?.color,
selectedOption?.darknessColor,
textColor,
)
" /><slot :name="`icon-right-${selectedOption?.value}`"></slot></span
><component
:is="iconsSet[openIcon]" :is="iconsSet[openIcon]"
:size="fontSize.slice(0, -2)" :size="fontSize.slice(0, -2)"
color="#62708c" :color="openIconColor ? convertThemeToColor(openIconColor, darknessOpenIcon) : '#62708c'"
:style="`width: ${fontSize}`" :style="`width: ${fontSize}`"
/> />
</button> </button>
...@@ -98,7 +97,7 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string | ...@@ -98,7 +97,7 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string |
]" ]"
> >
<div style="overflow: hidden"> <div style="overflow: hidden">
<p <SelectItem
@click.prevent="pickOption(option.value)" @click.prevent="pickOption(option.value)"
v-for="option of options" v-for="option of options"
:key="option.value" :key="option.value"
...@@ -111,22 +110,14 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string | ...@@ -111,22 +110,14 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string |
]" ]"
:style="`color: ${calcOptionColor(option.color, option.darknessColor, textColor)}; :style="`color: ${calcOptionColor(option.color, option.darknessColor, textColor)};
background-color: ${calcOptionColor(option.background, option.darknessBackground, backgroundColor)}`" background-color: ${calcOptionColor(option.background, option.darknessBackground, backgroundColor)}`"
:option="option"
:fontSize="fontSize"
:textColor="textColor"
> >
<slot :name="`icon-left-${option.value}`"></slot> <slot :name="`icon-left-${option.value}`"></slot>
<component <span :style="`font-size: ${fontSize}`">{{ option.label ?? option.value }}</span>
v-if="option.iconLeft"
:is="iconsSet[option.iconLeft]"
:size="fontSize.slice(0, -2)"
:color="calcOptionColor(option.iconColor ?? option.color, option.darknessColor, textColor)"
/><span>{{ option.label ?? option.value }}</span
><component
v-if="option.iconRight"
:is="iconsSet[option.iconRight]"
:size="fontSize.slice(0, -2)"
:color="calcOptionColor(option.iconColor ?? option.color, option.darknessColor, textColor)"
/>
<slot :name="`icon-right-${option.value}`"></slot> <slot :name="`icon-right-${option.value}`"></slot>
</p> </SelectItem>
</div> </div>
</div> </div>
</div> </div>
...@@ -146,7 +137,7 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string | ...@@ -146,7 +137,7 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string |
} }
.button { .button {
display: flex; display: flex;
padding: 7px; padding: v-bind(padding);
justify-content: space-between; justify-content: space-between;
gap: 10px; gap: 10px;
} }
...@@ -176,7 +167,7 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string | ...@@ -176,7 +167,7 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string |
display: flex; display: flex;
align-items: center; align-items: center;
gap: 5px; gap: 5px;
padding: 7px; padding: v-bind(padding);
} }
.option:hover { .option:hover {
filter: brightness(90%); filter: brightness(90%);
...@@ -193,36 +184,9 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string | ...@@ -193,36 +184,9 @@ const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string |
border-bottom-right-radius: 4px; border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px; border-bottom-left-radius: 4px;
} }
.disabled {
cursor: auto;
background-color: #e1e7f1 !important;
border-radius: 4px;
}
</style> </style>
<!--.list {-->
<!--position: relative;-->
<!--width: max-content;-->
<!--border: 1px solid v-bind(textColor);-->
<!--border-radius: 5px 5px 0 0;-->
<!--cursor: pointer;-->
<!--}-->
<!--.button {-->
<!--display: flex;-->
<!--padding: 7px;-->
<!--justify-content: space-between;-->
<!--gap: 10px;-->
<!--}-->
<!--.selected {-->
<!--display: flex;-->
<!--gap: 5px;-->
<!--}-->
<!--.options {-->
<!--position: absolute;-->
<!--top: 100%;-->
<!--width: 100%;-->
<!--border: 1px solid v-bind(textColor);-->
<!--border-top: none;-->
<!--border-bottom-left-radius: 5px;-->
<!--border-bottom-right-radius: 5px;-->
<!--display: grid;-->
<!--grid-template-rows: 0fr;-->
<!--opacity: 0;-->
<!--transition:-->
<!--all 0.2s ease-in-out,-->
<!--opacity 0.1s ease-in-out;-->
<!--}-->
<script setup lang="ts">
import { iconsSet } from '@/common/constants/icons';
import type { ISelectOption } from '@interfaces/componentsProp';
import type { TThemeColor } from '@interfaces/common';
import { convertThemeToColor } from '@helpers/common';
interface IProps {
option: ISelectOption | undefined;
fontSize: string;
textColor: string;
}
defineProps<IProps>();
const calcOptionColor = (color: TThemeColor | undefined, darknessColor: string | undefined, defaultColor: string) =>
color ? convertThemeToColor(color, darknessColor ?? '500') : defaultColor;
</script>
<template>
<span>
<slot :name="`icon-left-${option?.value}`"></slot>
<component
v-if="option?.iconLeft"
:is="iconsSet[option?.iconLeft]"
:size="fontSize.slice(0, -2)"
:color="calcOptionColor(option?.iconLeftColor ?? option?.color, option?.darknessColor, textColor)"
/>
<slot />
<component
v-if="option?.iconRight"
:is="iconsSet[option?.iconRight]"
:size="fontSize.slice(0, -2)"
:color="calcOptionColor(option?.iconRightColor ?? option?.color, option?.darknessColor, textColor)"
/>
<slot :name="`icon-right-${option?.value}`"></slot>
</span>
</template>
<style scoped></style>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment