vbv/client/src/components/ui/ItDropdownSelect.vue

104 lines
3.0 KiB
Vue

<script setup lang="ts">
import { Listbox, ListboxButton, ListboxOption, ListboxOptions } from "@headlessui/vue";
import { computed } from "vue";
interface DropdownSelectable {
id: number | string;
name: string;
iconName?: string;
}
// https://stackoverflow.com/questions/64775876/vue-3-pass-reactive-object-to-component-with-two-way-binding
interface Props {
modelValue: {
id: string | number;
name: string;
};
items: DropdownSelectable[];
}
const emit = defineEmits<{
(e: "update:modelValue", data: object): void;
}>();
const props = withDefaults(defineProps<Props>(), {
modelValue: () => {
return {
id: -1,
name: "",
};
},
items: () => [],
});
const dropdownSelected = computed<DropdownSelectable>({
get: () => props.modelValue,
set: (val) => emit("update:modelValue", val),
});
</script>
<template>
<Listbox v-model="dropdownSelected" as="div">
<div class="mt-1 relative w-full">
<ListboxButton
class="bg-white relative w-full border pl-5 pr-10 py-3 text-left cursor-default font-bold flex flex-row items-center"
>
<span v-if="dropdownSelected.iconName" class="mr-4">
<component :is="dropdownSelected.iconName"></component>
</span>
<span class="block truncate">{{ dropdownSelected.name }}</span>
<span
class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none"
>
<it-icon-arrow-down class="h-5 w-5" aria-hidden="true" />
</span>
</ListboxButton>
<transition
leave-active-class="transition ease-in duration-100"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<ListboxOptions
class="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
<ListboxOption
v-for="item in items"
:key="item.id"
v-slot="{ active, selected }"
as="template"
:value="item"
>
<li
:class="[
active ? 'text-white bg-blue-900' : 'text-black',
'cursor-default select-none relative py-2 pl-3 pr-9',
]"
class="flex flex-row items-center"
>
<span v-if="item.iconName" class="mr-4">
<component :is="item.iconName"></component>
</span>
<span
:class="[
dropdownSelected ? 'font-semibold' : 'font-normal',
'block truncate',
]"
>
{{ item.name }}
</span>
<span
v-if="dropdownSelected"
class="text-blue-900 absolute inset-y-0 right-0 flex items-center pr-4"
>
<it-icon-check v-if="selected" class="h-5 w-5" aria-hidden="true" />
</span>
</li>
</ListboxOption>
</ListboxOptions>
</transition>
</div>
</Listbox>
</template>