104 lines
3.0 KiB
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>
|