<template>
  <Teleport to="body">
    <transition name="drawer">
      <div v-if="visible" class="fixed inset-0 z-50">
        <button
          type="button"
          class="absolute inset-0 bg-black/45"
          :aria-label="t('common.close')"
          @click="emit('close')"
        ></button>
        <aside
          ref="drawerRef"
          role="dialog"
          aria-modal="true"
          :aria-label="title"
          tabindex="-1"
          class="absolute inset-y-0 right-0 flex w-full flex-col overflow-hidden bg-opacity-95 shadow-2xl backdrop-blur-xl sm:w-[30rem]"
          :class="[isDarkMode ? 'bg-gray-900' : 'bg-white']"
          @keydown="handleKeydown"
        >
          <div
            class="flex items-center justify-between border-b p-4 sm:p-6"
            :class="[isDarkMode ? 'border-gray-700' : 'border-gray-200']"
          >
            <h3 class="text-xl font-bold sm:text-2xl" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">
              {{ title }}
            </h3>
            <button
              type="button"
              class="flex h-11 w-11 items-center justify-center rounded-lg transition duration-200"
              :class="[isDarkMode ? 'text-gray-300 hover:bg-gray-800 hover:text-white' : 'text-gray-700 hover:bg-gray-100']"
              :aria-label="t('common.close')"
              :title="t('common.close')"
              @click="emit('close')"
            >
              <XIcon class="h-6 w-6" aria-hidden="true" />
            </button>
          </div>
          <div class="min-h-0 flex-1 overflow-y-auto" tabindex="0">
            <slot></slot>
          </div>
        </aside>
      </div>
    </transition>
  </Teleport>
</template>

<script setup lang="ts">
import { inject, nextTick, ref, watch } from 'vue'
import { XIcon } from 'lucide-vue-next'
import { useI18n } from 'vue-i18n'

const props = defineProps<{ visible: boolean; title: string }>()
const emit = defineEmits<{ close: [] }>()
const { t } = useI18n()
const isDarkMode = inject('isDarkMode')
const drawerRef = ref<HTMLElement>()
let returnFocusElement: HTMLElement | null = null
const focusableSelector =
  'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'

const handleKeydown = (event: KeyboardEvent) => {
  if (event.key === 'Escape') {
    event.preventDefault()
    emit('close')
    return
  }
  if (event.key !== 'Tab' || !drawerRef.value) return
  const focusable = [...drawerRef.value.querySelectorAll<HTMLElement>(focusableSelector)]
  const first = focusable[0]
  const last = focusable[focusable.length - 1]
  if (!first || !last) return
  if (event.shiftKey && document.activeElement === first) {
    event.preventDefault()
    last.focus()
  } else if (!event.shiftKey && document.activeElement === last) {
    event.preventDefault()
    first.focus()
  }
}

watch(
  () => props.visible,
  async (visible) => {
    if (visible) {
      returnFocusElement = document.activeElement instanceof HTMLElement ? document.activeElement : null
      await nextTick()
      const first = drawerRef.value?.querySelector<HTMLElement>(focusableSelector)
      ;(first || drawerRef.value)?.focus()
    } else {
      await nextTick()
      returnFocusElement?.focus()
      returnFocusElement = null
    }
  }
)
</script>

<style scoped>
.drawer-enter-active,
.drawer-leave-active {
  transition: opacity 0.24s ease;
}
.drawer-enter-active aside,
.drawer-leave-active aside {
  transition: transform 0.24s ease;
}
.drawer-enter-from,
.drawer-leave-to {
  opacity: 0;
}
.drawer-enter-from aside,
.drawer-leave-to aside {
  transform: translateX(100%);
}
@media (prefers-reduced-motion: reduce) {
  .drawer-enter-active,
  .drawer-leave-active,
  .drawer-enter-active aside,
  .drawer-leave-active aside {
    transition-duration: 0.01ms;
  }
}
</style>
