121 lines
3.6 KiB
Vue
121 lines
3.6 KiB
Vue
<script setup lang="ts">
|
|
interface DataItem {
|
|
id: number;
|
|
value: string;
|
|
}
|
|
|
|
import { ref, computed } from 'vue'
|
|
import {
|
|
Combobox,
|
|
ComboboxInput,
|
|
ComboboxButton,
|
|
ComboboxOptions,
|
|
ComboboxOption,
|
|
TransitionRoot
|
|
} from '@headlessui/vue'
|
|
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/vue/20/solid'
|
|
|
|
const props = defineProps({
|
|
placeholder: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
data: {
|
|
type: Array as () => DataItem[],
|
|
default: []
|
|
}
|
|
})
|
|
|
|
const data: DataItem[] = [
|
|
{id: 0, value: props.placeholder},
|
|
...props.data
|
|
]
|
|
|
|
let selected = ref(data[0])
|
|
let query = ref('')
|
|
|
|
let filteredData = computed(() =>
|
|
query.value === ''
|
|
? data
|
|
: data.filter((item: DataItem) =>
|
|
item.value
|
|
.toLowerCase()
|
|
.replace(/\s+/g, '')
|
|
.includes(query.value.toLowerCase().replace(/\s+/g, ''))
|
|
)
|
|
)
|
|
|
|
const show = ref(false)
|
|
</script>
|
|
|
|
<template>
|
|
<Combobox v-model="selected" v-slot="{ open }">
|
|
<div class="relative mt-1">
|
|
<div
|
|
class="relative w-full cursor-default overflow-hidden rounded-lg bg-gray-200 text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm"
|
|
>
|
|
<ComboboxInput
|
|
class="w-full border-none py-2 pl-3 pr-10 bg-gray-200 text-sm leading-5 text-gray-900 focus:ring-0"
|
|
:displayValue="(item: any) => (show || open ? '' : item.value)"
|
|
@change="query = $event.target.value"
|
|
@click="show = true"
|
|
@blur="show = false"
|
|
defaultValue="sasa"
|
|
/>
|
|
|
|
<ComboboxButton id="Test" class="absolute inset-y-0 right-0 flex items-center pr-2">
|
|
<ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" />
|
|
</ComboboxButton>
|
|
</div>
|
|
<TransitionRoot
|
|
:show="show || open"
|
|
leave="transition ease-in duration-100"
|
|
leaveFrom="opacity-100"
|
|
leaveTo="opacity-0"
|
|
@after-leave="query = ''"
|
|
>
|
|
<ComboboxOptions
|
|
class="absolute mt-1 z-10 max-h-60 w-full overflow-auto rounded-md bg-gray-200 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
|
|
>
|
|
<div
|
|
v-if="filteredData.length === 0 && query !== ''"
|
|
class="relative cursor-default select-none py-2 px-4 text-gray-700"
|
|
>
|
|
Nothing found.
|
|
</div>
|
|
|
|
<ComboboxOption
|
|
v-for="item in filteredData"
|
|
as="template"
|
|
:key="item.id"
|
|
:value="item"
|
|
v-slot="{ selected, active }"
|
|
>
|
|
<li
|
|
class="relative cursor-default select-none py-2 pl-10 pr-4"
|
|
:class="{
|
|
'bg-teal-600 text-white': active,
|
|
'text-gray-900': !active
|
|
}"
|
|
>
|
|
<span
|
|
class="block truncate"
|
|
:class="{ 'font-medium': selected, 'font-normal': !selected }"
|
|
>
|
|
{{ item.value }}
|
|
</span>
|
|
<span
|
|
v-if="selected"
|
|
class="absolute inset-y-0 left-0 flex items-center pl-3"
|
|
:class="{ 'text-white': active, 'text-teal-600': !active }"
|
|
>
|
|
<CheckIcon class="h-5 w-5" aria-hidden="true" />
|
|
</span>
|
|
</li>
|
|
</ComboboxOption>
|
|
</ComboboxOptions>
|
|
</TransitionRoot>
|
|
</div>
|
|
</Combobox>
|
|
</template>
|