<script setup lang="ts">
import type {Ref} from "vue";

type Placement = 'left' | 'right'

interface Props {
  placement: Placement
}

const props = withDefaults(defineProps<Props>(), {
  placement: "right"
})

const expanded = ref(false)
const menuWidth = ref(0)

const onExpand = async () => {
  expanded.value = !expanded.value

  if (expanded.value) await updatePosition()
}

const menu = ref<HTMLElement | null>(null)
const card = ref<HTMLElement | null>(null)

async function updatePosition() {
  await nextTick()
  if (!menu.value || !card.value) return

  menuWidth.value = card.value.offsetWidth
  const {left, right} = menu.value.getBoundingClientRect()

  if (props.placement === 'right') {
    menuWidth.value < right ? card.value.style.right = '0px' : definePosition("right")
  } else {
    window.innerWidth - left > menuWidth.value ? card.value.style.left = '0px': definePosition("left")
  }

  function definePosition(placement: Placement) {
    if (!card.value) return
    if (menuWidth.value > window.innerWidth) {
      card.value.style.position = "fixed"
      card.value.style.left = "0"
      card.value.style.maxWidth = "100%"
    } else {
      if (placement === 'left') {
        card.value.style.left = - (left - (window.innerWidth - menuWidth.value)) + "px"
      } else {
        card.value.style.left = - left + "px"
      }
    }
  }
}


const onClickOutside = (element: Ref, callback: Function) => {
  nextTick(() => {
    document.addEventListener('mousedown', e => {
      if (!element.value) return
      if (!element.value.contains(e.target)) callback()
    })
  })
}

onMounted(() => {
  onClickOutside(menu, () => {
    expanded.value = false
  })
  updatePosition()

  window.addEventListener("resize", () => {
    expanded.value = false
  })
})
</script>

<template>
  <div
    ref="menu"
    class="relative"
  >
    <slot
      :on-expand="onExpand"
      :expanded="expanded"
    />
    <transition name="menu">
      <div
        ref="card"
        v-if="expanded"
        class="absolute z-20 mt-5 lg:mt-4 shadow-md rounded-[0.5rem] top-[44px] lg:top-[initial]"
      >
        <slot
          name="content"
          :on-expand="onExpand"
        >
          content
        </slot>
      </div>
    </transition>
  </div>
</template>

<style scoped lang="scss">
.menu-enter-active,
.menu-leave-active {
  animation: 0.12s overlay-transition cubic-bezier(0,0,0.2,1);
}

.menu-leave-active {
  animation-direction: reverse;
}

@keyframes overlay-transition {
  from {
    opacity: 0;
    transform: translateY(-16px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}
</style>
