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

99 lines
3.0 KiB
Vue

<script setup lang="ts">
import type { DropdownSelectable } from "@/types";
import { Listbox, ListboxButton, ListboxOption, ListboxOptions } from "@headlessui/vue";
import { computed } from "vue";
// 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="relative mt-1 w-full">
<ListboxButton
class="relative flex w-full cursor-default flex-row items-center border bg-white py-3 pl-5 pr-10 text-left font-bold"
>
<span v-if="dropdownSelected.iconName" class="mr-4">
<component :is="dropdownSelected.iconName"></component>
</span>
<span class="block truncate">{{ dropdownSelected.name }}</span>
<span
class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
>
<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 max-h-60 w-full overflow-auto bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 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 ? 'bg-blue-900 text-white' : 'text-black',
'relative cursor-default select-none 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="absolute inset-y-0 right-0 flex items-center pr-4 text-blue-900"
>
<it-icon-check v-if="selected" class="h-5 w-5" aria-hidden="true" />
</span>
</li>
</ListboxOption>
</ListboxOptions>
</transition>
</div>
</Listbox>
</template>