Files
hossein-por-shop/frontend/components/global/Select.vue
T
2025-04-08 23:28:38 +03:30

114 lines
3.9 KiB
Vue

<script setup lang="ts">
// types
type Props = {
variant?: "solid" | "outlined";
disabled?: boolean;
modelValue: string;
error?: boolean;
options?: string[];
placeholder?: string;
triggerRootClass?: string;
loading?: boolean;
};
type Emits = {
"update:modelValue": [value: string];
};
// props
const props = withDefaults(defineProps<Props>(), {
variant: "solid",
disabled: false,
placeholder: "وارد نشده",
loading: false,
});
const { modelValue, variant, error, triggerRootClass } = toRefs(props);
// emit
const emit = defineEmits<Emits>();
// computed
const selectedValue = computed({
get: () => modelValue.value ?? undefined,
set: (value: string) => emit("update:modelValue", value),
});
const classes = computed(() => {
return [
"flex items-center text-black justify-between cursor-text transition-all border-[1.5px] gap-3 grow-0 typo-label-md px-4 py-2.5 lg:py-3.5 selection:bg-slate-100 rounded-md lg:rounded-100 flex-1 w-full outline-none",
{
"input-solid": variant.value === "solid",
"input-outlined": variant.value === "outlined",
"input-effects": !error.value,
[variant.value === "solid"
? "input-solid-error"
: "input-outlined-error"]: error.value,
},
triggerRootClass.value,
];
});
</script>
<template>
<SelectRoot
v-model="selectedValue"
dir="rtl"
:disabled="disabled || loading"
>
<SelectTrigger :class="classes">
<slot v-if="!!$slots.trigger" name="trigger" />
<SelectValue
v-else
:placeholder="placeholder"
v-bind="$attrs"
:class="selectedValue ? '!text-black' : 'text-slate-400'"
class="font-iran-yekan-x text-xs lg:text-sm text-start placeholder-slate-400"
/>
<Icon
:name="loading ? 'svg-spinners:3-dots-fade' : 'bi:chevron-down'"
size="16"
/>
</SelectTrigger>
<SelectPortal>
<SelectContent
data-side="bottom"
class="min-w-[160px] w-full bg-slate-50 border-slate-200 rounded-lg border shadow-sm will-change-[opacity,transform] data-[side=top]:animate-slide-down-fade data-[side=right]:animate-slide-left-fade data-[side=bottom]:animate-slide-up-fade data-[side=left]:animate-slide-right-fade z-[9999]"
:side-offset="5"
>
<SelectViewport class="p-[5px]">
<slot v-if="!!$slots.content" name="content" />
<SelectGroup v-else>
<SelectItem
v-for="(option, index) in options"
:key="index"
class="text-xs leading-none w-full rounded-sm py-5 flex items-center justify-between h-[25px] pr-[12px] relative select-none data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-slate-300 data-[highlighted]:text-black"
:value="option"
>
<SelectItemIndicator
class="absolute left-0 w-[25px] inline-flex items-center justify-center"
>
<Icon name="bi:check" size="20" />
</SelectItemIndicator>
<SelectItemText
class="text-end font-iran-yekan-x text-sm"
>
{{ option }}
</SelectItemText>
</SelectItem>
</SelectGroup>
</SelectViewport>
</SelectContent>
</SelectPortal>
</SelectRoot>
</template>
<style scoped></style>