<template>
    <div 
        class="trigger" 
        ref="trigger"
        @mouseenter="onMouseEnter" 
        @touchstart="onMouseEnter"
        @mouseleave="onMouseLeave">
        <slot />
    </div>
    <Teleport to="#dropdowns">
        <div 
            ref="dropdown" 
            class="dropdown" 
            :class="{ visible }"
            @mouseleave="visible = false">
            <slot name="content" />
        </div>
    </Teleport>
</template>

<script setup lang="ts">
    import { computed, ref } from 'vue';

    const trigger = ref<HTMLDivElement>();
    const dropdown = ref<HTMLDivElement>();

    const visible = ref(false);
    const top = ref('0px');
    const left = ref('0px');
    const effectiveTop = computed(() => visible.value ? top.value : 0);
    const effectiveLeft = computed(() => visible.value ? left.value : 0);

    function onMouseEnter(e: MouseEvent | TouchEvent) {
        visible.value = true;
        if (trigger.value && dropdown.value) {
            const rectTrigger = trigger.value.getBoundingClientRect();
            const rectDropdown = dropdown.value.getBoundingClientRect();
            console.log(rectDropdown);
            top.value = Math.min(window.innerHeight - rectDropdown.height, rectTrigger.top) + 'px';
            left.value = rectTrigger.width + rectTrigger.left + 'px';
        }
        window.addEventListener('touchstart', onWindowClick);
        window.addEventListener('click', onWindowClick);
    }

    function outside(e: MouseEvent, el: HTMLElement) {
        const rect = el.getBoundingClientRect();
        return e.clientX < rect.x 
            || e.clientX > rect.x + rect.width
            || e.clientY < rect.y 
            || e.clientY > rect.y + rect.height;
    }

    function onWindowClick(e: MouseEvent | TouchEvent) {
        if (visible.value && e instanceof MouseEvent && dropdown.value && !outside(e, dropdown.value)) {
            visible.value = false;
            window.removeEventListener('touchstart', onWindowClick);
            window.removeEventListener('click', onWindowClick);
        }
    }

    function onMouseLeave(e: MouseEvent) {
        if (trigger.value && outside(e, trigger.value) && dropdown.value && outside(e, dropdown.value)) {
            visible.value = false;
        }
    }
</script>

<style scoped lang="scss">
.dropdown {
    position: absolute;
    top: v-bind(effectiveTop);
    left: v-bind(effectiveLeft);
    visibility: hidden;

    &.visible {
        visibility: visible;
    }

    &:hover {
        visibility: visible;
    }
}
</style>
