feat: Add the new select menu component (#10445)
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import SelectMenu from './SelectMenu.vue';
|
||||||
|
|
||||||
|
const sampleOptions = [
|
||||||
|
{ label: 'Option 1', value: 'option1' },
|
||||||
|
{ label: 'Option 2', value: 'option2' },
|
||||||
|
{ label: 'Option 3', value: 'option3' },
|
||||||
|
{ label: 'Option 4', value: 'option4' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const selectedValue = ref('option1');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Story
|
||||||
|
title="Components/SelectMenu"
|
||||||
|
:layout="{ type: 'grid', width: '400px' }"
|
||||||
|
>
|
||||||
|
<Variant title="Default">
|
||||||
|
<div class="flex flex-col gap-4 p-4 h-[400px]">
|
||||||
|
<SelectMenu
|
||||||
|
v-model="selectedValue"
|
||||||
|
:options="sampleOptions"
|
||||||
|
:label="sampleOptions.find(opt => opt.value === selectedValue)?.label"
|
||||||
|
/>
|
||||||
|
<div class="text-sm">Selected value: {{ selectedValue }}</div>
|
||||||
|
</div>
|
||||||
|
</Variant>
|
||||||
|
|
||||||
|
<Variant title="With Many Options">
|
||||||
|
<div class="flex flex-col gap-4 p-4 h-[400px]">
|
||||||
|
<SelectMenu
|
||||||
|
v-model="selectedValue"
|
||||||
|
:options="
|
||||||
|
Array.from({ length: 10 }, (_, i) => ({
|
||||||
|
label: `Option ${i + 1}`,
|
||||||
|
value: `value${i + 1}`,
|
||||||
|
}))
|
||||||
|
"
|
||||||
|
label="Select from many"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Variant>
|
||||||
|
</Story>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import Button from 'dashboard/components-next/button/Button.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
|
const isOpen = ref(false);
|
||||||
|
|
||||||
|
const labelValue = computed(() => props.label);
|
||||||
|
|
||||||
|
const toggleMenu = () => {
|
||||||
|
isOpen.value = !isOpen.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelect = value => {
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
isOpen.value = false;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-on-clickaway="() => (isOpen = false)"
|
||||||
|
class="relative flex flex-col gap-1 w-fit"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon="i-lucide-chevron-down"
|
||||||
|
size="sm"
|
||||||
|
trailing-icon
|
||||||
|
color="slate"
|
||||||
|
variant="faded"
|
||||||
|
class="!w-fit"
|
||||||
|
:class="{ 'dark:!bg-n-alpha-2 !bg-n-slate-9/20': isOpen }"
|
||||||
|
:label="labelValue"
|
||||||
|
@click="toggleMenu"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="isOpen"
|
||||||
|
class="absolute ltr:left-full rtl:right-full select-none max-w-48 ltr:ml-1 rtl:mr-1 flex flex-col gap-1 bg-n-alpha-3 backdrop-blur-[100px] p-1 top-0 shadow-lg rounded-lg border border-n-weak"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
v-for="option in options"
|
||||||
|
:key="option.value"
|
||||||
|
:label="option.label"
|
||||||
|
:icon="option.value === modelValue ? 'i-lucide-check' : ''"
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
color="slate"
|
||||||
|
trailing-icon
|
||||||
|
class="!justify-end !px-2.5 !h-7"
|
||||||
|
:class="{ '!bg-n-alpha-2': option.value === modelValue }"
|
||||||
|
@click="handleSelect(option.value)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user