<!-- SPEC: PJ2026-010405 云端控制台. -->
<!-- Implementation reference: draft-2026-07-13-p0-cloud-console. -->
<!-- Responsibility: 提供支持 topmost 栈、焦点圈闭和滚动锁定的 Dialog/Drawer。 -->

<script setup lang="ts">
import { X } from "lucide-vue-next";
import { computed, ref, toRef } from "vue";
import { nextOverlayId, useOverlaySurface } from "@/composables/useOverlaySurface";

const props = withDefaults(defineProps<{
  open: boolean;
  title: string;
  description?: string;
  kind?: "dialog" | "drawer";
  placement?: "left" | "right";
  wide?: boolean;
  fullscreen?: boolean;
  closeOnBackdrop?: boolean;
  busy?: boolean;
  initialFocus?: string;
  surfaceClass?: string;
}>(), {
  description: "",
  kind: "dialog",
  placement: "right",
  wide: false,
  fullscreen: false,
  closeOnBackdrop: true,
  busy: false,
  initialFocus: "",
  surfaceClass: ""
});
const emit = defineEmits<{ close: [] }>();
const surface = ref<HTMLElement | null>(null);
const titleId = nextOverlayId("console-overlay-title");
const descriptionId = nextOverlayId("console-overlay-description");
const describedBy = computed(() => props.description ? descriptionId : undefined);
const overlay = useOverlaySurface(toRef(props, "open"), surface, close, {
  initialFocusSelector: props.initialFocus || undefined
});

function close(): void {
  if (!props.busy && overlay.isTopmost()) emit("close");
}
</script>

<template>
  <Teleport to="body">
    <div
      v-if="open"
      class="console-overlay-backdrop"
      :class="{ 'console-drawer-backdrop': kind === 'drawer' }"
      :data-fullscreen="kind === 'dialog' && fullscreen ? 'true' : undefined"
      role="presentation"
      @mousedown.self="closeOnBackdrop ? close() : undefined"
    >
      <component
        :is="kind === 'drawer' ? 'aside' : 'section'"
        ref="surface"
        class="console-overlay-surface"
        :class="[{ 'console-drawer': kind === 'drawer' }, surfaceClass]"
        :data-placement="kind === 'drawer' ? placement : undefined"
        :data-wide="kind === 'dialog' && wide ? 'true' : undefined"
        :data-fullscreen="kind === 'dialog' && fullscreen ? 'true' : undefined"
        role="dialog"
        aria-modal="true"
        :aria-labelledby="titleId"
        :aria-describedby="describedBy"
        :aria-busy="busy ? 'true' : 'false'"
        tabindex="-1"
      >
        <header class="console-overlay-header">
          <div class="console-overlay-title-stack">
            <h2 :id="titleId" :title="title">{{ title }}</h2>
            <p v-if="description" :id="descriptionId">{{ description }}</p>
          </div>
          <button
            type="button"
            class="console-overlay-close"
            :disabled="busy"
            title="关闭"
            aria-label="关闭"
            @click="close"
          >
            <X :size="18" aria-hidden="true" />
          </button>
        </header>
        <div class="console-overlay-body"><slot /></div>
        <footer v-if="$slots.footer" class="console-overlay-footer">
          <slot name="footer" />
        </footer>
      </component>
    </div>
  </Teleport>
</template>
