70 lines
1.8 KiB
Vue
70 lines
1.8 KiB
Vue
<script setup lang="ts">
|
|
import {ChevronDownIcon, ChevronUpIcon} from '@heroicons/vue/24/outline';
|
|
import {vOnClickOutside} from '@vueuse/components';
|
|
import {useEventListener} from '@vueuse/core';
|
|
import {ref} from 'vue';
|
|
|
|
interface Props {
|
|
inline?: boolean;
|
|
autoClose?: boolean;
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
inline: false,
|
|
autoClose: true
|
|
})
|
|
|
|
const isOpen = ref(false);
|
|
|
|
const doAutoClose = () => {
|
|
if (props.autoClose) {
|
|
isOpen.value = false;
|
|
}
|
|
}
|
|
|
|
useEventListener('keyup', e => {
|
|
if (e.key === 'Escape') {
|
|
doAutoClose();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="dropdown" :class="{'dropdown-open': isOpen, 'dropdown-close': !isOpen}" v-on-click-outside="doAutoClose">
|
|
<button @click="isOpen = !isOpen" class="cursor-pointer">
|
|
<Transition
|
|
enter-active-class="transition-transform"
|
|
enter-from-class="rotate-180"
|
|
leave-active-class="hidden"
|
|
leave-to-class="rotate-180">
|
|
<ChevronDownIcon v-if="!isOpen" class="chevron" />
|
|
<ChevronUpIcon v-else class="chevron" />
|
|
</Transition>
|
|
<slot name="button" />
|
|
</button>
|
|
|
|
<Transition
|
|
enter-active-class="transition-opacity"
|
|
enter-from-class="opacity-0"
|
|
leave-from-class="transition-opacity"
|
|
leave-to-class="opacity-0">
|
|
<div v-if="inline && isOpen">
|
|
<slot />
|
|
</div>
|
|
<div v-else-if="isOpen" class="relative">
|
|
<div class="z-10 divide-y rounded-b-md absolute">
|
|
<slot />
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
@reference "@/style.css";
|
|
|
|
.chevron {
|
|
@apply w-4 h-4 me-1;
|
|
}
|
|
</style>
|