152 lines
5.5 KiB
Vue
152 lines
5.5 KiB
Vue
<script setup lang="ts">
|
|
// types
|
|
|
|
type OptionChildren = {
|
|
id: number | string;
|
|
name: string;
|
|
slug: string;
|
|
};
|
|
|
|
type Option = {
|
|
name: string;
|
|
slug: string;
|
|
children: OptionChildren[];
|
|
};
|
|
|
|
type Props = {
|
|
options: Option[];
|
|
modelValue: number | string;
|
|
placeholder?: string;
|
|
};
|
|
|
|
// props
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
placeholder: "جست و جو",
|
|
});
|
|
|
|
const { options, modelValue } = toRefs(props);
|
|
|
|
// emit
|
|
|
|
const emit = defineEmits(["update:modelValue"]);
|
|
|
|
// state
|
|
|
|
const value = ref<OptionChildren | Option>();
|
|
|
|
// watch
|
|
|
|
watch(
|
|
() => value.value,
|
|
(newValue) => {
|
|
if (!!newValue) {
|
|
emit("update:modelValue", newValue.slug);
|
|
}
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => modelValue.value,
|
|
(newValue) => {
|
|
let target;
|
|
target = newValue?.toString().startsWith("category")
|
|
? options.value.find((child) => child.slug == newValue)
|
|
: options.value.flatMap((option) => option.children).find((child) => child.slug == newValue);
|
|
|
|
value.value = target || undefined;
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<ComboboxRoot
|
|
class="relative"
|
|
dir="rtl"
|
|
v-model="value"
|
|
>
|
|
<ComboboxAnchor
|
|
class="w-full inline-flex items-center justify-between rounded-xl border-[1.5px] border-slate-200 focus:border-slate-800 leading-none px-4 py-2.5 lg:py-3.5 gap-[5px] bg-slate-50 text-black hover:border-black transition-all data-[placeholder]:text-black/80 text-xs lg:text-sm placeholder-slate-400 placeholder:text-xs lg:placeholder:text-sm placeholder:font-normal outline-none"
|
|
>
|
|
<ComboboxInput
|
|
:display-value="(v) => (!!v ? v.name : '')"
|
|
class="!bg-transparent outline-none text-black h-full w-full selection:bg-slate-100 placeholder-slate-400"
|
|
:placeholder="placeholder"
|
|
/>
|
|
<ComboboxTrigger class="cursor-pointer">
|
|
<Icon
|
|
name="ci:chevron-down"
|
|
class="size-5"
|
|
/>
|
|
</ComboboxTrigger>
|
|
</ComboboxAnchor>
|
|
|
|
<ComboboxContent
|
|
class="absolute z-10 w-full max-h-[25rem] mt-4 bg-slate-50 overflow-hidden rounded-xl shadow-sm border border-slate-200 will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=bottom]:animate-slideUpAndFade"
|
|
>
|
|
<ComboboxViewport class="p-[1rem]">
|
|
<ComboboxEmpty class="placeholder-slate-400 text-xs font-medium text-center py-5" />
|
|
|
|
<template
|
|
v-for="(group, index) in options"
|
|
:key="group.name"
|
|
>
|
|
<ComboboxGroup>
|
|
<ComboboxSeparator
|
|
v-if="index !== 0"
|
|
class="h-6"
|
|
/>
|
|
|
|
<ComboboxLabel
|
|
class="flex items-center justify-between px-[1.2rem] w-full max-lg:text-sm text-black bg-slate-200/50 leading-[25px] py-2 lg:py-4 rounded-lg"
|
|
>
|
|
<ComboboxItem
|
|
:value="group"
|
|
class="w-full text-sm cursor-pointer leading-none text-slate-700 rounded-md flex items-center relative select-none data-[disabled]:text-slate-50 data-[disabled]:pointer-events-none"
|
|
>
|
|
<div class="w-full flex items-center justify-between gap-2 max-lg:text-xs">
|
|
<span>
|
|
{{ group.name }}
|
|
</span>
|
|
<Icon
|
|
name="ci:delivery-boxes"
|
|
class="text-lg"
|
|
/>
|
|
</div>
|
|
</ComboboxItem>
|
|
</ComboboxLabel>
|
|
|
|
<ComboboxItem
|
|
v-for="option in group.children"
|
|
:key="option.name"
|
|
:value="option"
|
|
class="text-sm cursor-pointer leading-none text-slate-700 my-1.5 rounded-md hover:bg-slate-200/25 flex items-center py-3 px-[1.2rem] relative select-none data-[disabled]:text-slate-50 data-[disabled]:pointer-events-none"
|
|
>
|
|
<ComboboxItemIndicator
|
|
class="absolute left-3 w-[25px] inline-flex items-center justify-center"
|
|
>
|
|
<Icon
|
|
name="ci:checkmark"
|
|
size="18"
|
|
/>
|
|
</ComboboxItemIndicator>
|
|
<div class="flex items-center gap-2 max-lg:text-xs">
|
|
<Icon
|
|
name="ci:minus"
|
|
class="opacity-50"
|
|
/>
|
|
<span>
|
|
{{ option.name }}
|
|
</span>
|
|
</div>
|
|
</ComboboxItem>
|
|
</ComboboxGroup>
|
|
</template>
|
|
</ComboboxViewport>
|
|
</ComboboxContent>
|
|
</ComboboxRoot>
|
|
</template>
|
|
|
|
<style scoped></style>
|