<script setup lang="ts">
import { nextTick, onBeforeUnmount, onMounted, ref } from "vue";

const props = withDefaults(defineProps<{ title: string; wide?: boolean; closeOnBackdrop?: boolean }>(), {
  wide: false,
  closeOnBackdrop: true,
});
const emit = defineEmits<{ close: [] }>();

const panel = ref<HTMLElement | null>(null);
const previouslyFocused = document.activeElement as HTMLElement | null;

function focusableElements(): HTMLElement[] {
  if (!panel.value) return [];
  return Array.from(
    panel.value.querySelectorAll<HTMLElement>(
      'button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href], [tabindex]:not([tabindex="-1"])',
    ),
  );
}

function handleKeydown(event: KeyboardEvent): void {
  if (event.key === "Escape") {
    emit("close");
    return;
  }
  if (event.key !== "Tab") return;
  const focusable = focusableElements();
  const first = focusable[0];
  const last = focusable.at(-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();
  }
}

function backdrop(event: MouseEvent): void {
  if (props.closeOnBackdrop && event.target === event.currentTarget) emit("close");
}

onMounted(async () => {
  document.addEventListener("keydown", handleKeydown);
  await nextTick();
  focusableElements()[0]?.focus();
});

onBeforeUnmount(() => {
  document.removeEventListener("keydown", handleKeydown);
  previouslyFocused?.focus();
});
</script>

<template>
  <Teleport to="body">
    <div class="dialog-backdrop" @mousedown="backdrop">
      <section ref="panel" class="dialog" :class="{ 'dialog-wide': wide }" role="dialog" aria-modal="true" :aria-label="title">
        <header class="dialog-header">
          <h2 class="dialog-title">{{ title }}</h2>
          <button class="icon-button dialog-close" type="button" aria-label="关闭" title="关闭" @click="emit('close')">×</button>
        </header>
        <div class="dialog-body"><slot /></div>
      </section>
    </div>
  </Teleport>
</template>
