1
0
2024-07-15 19:51:14 +00:00

94 lines
1.4 KiB
Vue

<template>
<slot
name="trigger"
:is-open="openValue"
:toggle="toggle"
:close="close"
/>
<div
ref="popup"
class="popup"
:class="{
'is-open': openValue,
'has-overflow': hasOverflow && openValue
}"
>
<slot
name="content"
:is-open="openValue"
:toggle="toggle"
:close="close"
/>
</div>
</template>
<script setup lang="ts">
import {ref, watchEffect} from 'vue'
import {onClickOutside} from '@vueuse/core'
const props = withDefaults(defineProps<{
hasOverflow?: boolean
open?: boolean
}>(), {
hasOverflow: false,
open: false,
})
const emit = defineEmits<{
'update:open': [open: boolean]
}>()
defineSlots<{
trigger(props: {
isOpen: boolean,
toggle: () => boolean,
close: () => void,
}) : void
content(props: {
isOpen: boolean,
toggle: () => boolean,
close: () => void
}): void
}>()
const openValue = ref(props.open)
watchEffect(() => {
openValue.value = props.open
})
function close() {
if (!openValue.value) {
return
}
openValue.value = false
emit('update:open', false)
}
function toggle() {
openValue.value = !openValue.value
emit('update:open', openValue.value)
return openValue.value
}
const popup = ref<HTMLElement | null>(null)
onClickOutside(popup, () => close())
</script>
<style scoped lang="scss">
.popup {
transition: opacity $transition;
opacity: 0;
height: 0;
overflow: hidden;
position: absolute;
top: 1rem;
z-index: 100;
&.is-open {
opacity: 1;
height: auto;
}
}
</style>