<script setup lang="ts" generic="T">
import {vOnClickOutside} from '@vueuse/components';
import {useVirtualList} from '@vueuse/core';
import {computed, ref} from 'vue';

interface Props {
  items: T[];
  itemHeight?: number;
}

const props = withDefaults(defineProps<Props>(), {
    itemHeight: 24,
});

const modelValue = defineModel<T>();

const isOpen = ref(false);
const currentIndex = ref(-1);
const {list, scrollTo, containerProps, wrapperProps} = useVirtualList(computed(() => props.items), {
    itemHeight: () => props.itemHeight,
    overscan: 3,
});

const moveDown = () => {
    currentIndex.value = currentIndex.value >= props.items.length - 1 ? 0 : currentIndex.value + 1;
    scrollTo(currentIndex.value);
};
const moveUp = () => {
    currentIndex.value = currentIndex.value <= 0 ? props.items.length - 1 : currentIndex.value - 1;
    scrollTo(currentIndex.value);
};
const select = (item?: T) => {
    modelValue.value = item;
    currentIndex.value = -1;
    isOpen.value = false;
};
const submit = () => {
    if (currentIndex.value >= 0 && currentIndex.value < props.items.length) {
        select(props.items[currentIndex.value]);
    } else if (modelValue.value === undefined && props.items.length > 0) {
        select(props.items[0]);
    }
};
</script>

<template>
    <div @click="isOpen = true" v-on-click-outside="() => isOpen = false">
      <div class="fake-input" @keyup.enter="submit" @keyup.down="moveDown" @keyup.up="moveUp">
        <slot name="input" :value="modelValue" />
      </div>
        <div v-if="isOpen && items.length" class="z-20 absolute">
            <div v-bind="containerProps" class="rounded-b" style="height: 300px">
                <div v-bind="wrapperProps">
                    <div v-for="s in list" :key="s.index"
                        class="hover:bg-slate-700 cursor-pointer"
                        :class="s.index === currentIndex ? 'bg-emerald-500' : 'bg-slate-500'"
                        @click.stop="select(s.data)">
                        <slot name="item" :item="s.data" />
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<style scoped>
@reference "@/style.css";
.fake-input {
    @apply flex border bg-slate-500 rounded px-1 py-0.5;
}
</style>