<!-- 卡片操作菜单，统一项目卡片的编辑和删除入口。 -->
<script setup lang="ts">
import { onBeforeUnmount, ref } from "vue";
import { MoreHorizontal, Pencil, Trash2 } from "@/icons";

withDefaults(defineProps<{ label?: string; editLabel?: string; deleteLabel?: string }>(), {
  label: "更多操作",
  editLabel: "编辑",
  deleteLabel: "删除",
});
const emit = defineEmits<{ edit: []; delete: [] }>();
const dropdown = ref<{ handleClose: () => void }>();
const trigger = ref<HTMLButtonElement>();
const menuVisible = ref(false);
const popperOptions = { modifiers: [{ name: "offset", options: { offset: [20, -1] } }] };
/** 将菜单命令转换为编辑或删除事件。 */
function handleCommand(command: string) {
  if (command === "edit") emit("edit");
  if (command === "delete") emit("delete");
}
/** 移除菜单关闭监听，避免组件卸载后残留全局事件。 */
function removeDismissListeners() {
  document.removeEventListener("pointerdown", handlePointerDown, true);
  document.removeEventListener("focusin", handleFocusIn, true);
  window.removeEventListener("blur", closeMenu);
}
/** 关闭当前操作菜单。 */
function closeMenu() {
  dropdown.value?.handleClose();
  trigger.value?.blur();
  removeDismissListeners();
}
/** 判断事件目标是否位于菜单触发器或弹层内部。 */
function isInsideMenu(target: EventTarget | null) {
  return (
    target instanceof Element && (trigger.value?.contains(target) || Boolean(target.closest(".card-action-popper")))
  );
}
/** 点击菜单外部时关闭菜单。 */
function handlePointerDown(event: PointerEvent) {
  if (!isInsideMenu(event.target)) closeMenu();
}
/** 焦点移出菜单时关闭菜单。 */
function handleFocusIn(event: FocusEvent) {
  if (!isInsideMenu(event.target)) closeMenu();
}
/** 根据弹层可见状态安装或移除关闭监听。 */
function handleVisibleChange(visible: boolean) {
  menuVisible.value = visible;
  removeDismissListeners();
  if (!visible) {
    trigger.value?.blur();
    return;
  }
  document.addEventListener("pointerdown", handlePointerDown, true);
  document.addEventListener("focusin", handleFocusIn, true);
  window.addEventListener("blur", closeMenu);
}
onBeforeUnmount(removeDismissListeners);
</script>

<template>
  <el-dropdown
    ref="dropdown"
    class="card-action-menu"
    :class="{ 'is-open': menuVisible }"
    trigger="click"
    placement="bottom-end"
    :show-arrow="false"
    :popper-options="popperOptions"
    popper-class="card-action-popper"
    @command="handleCommand"
    @visible-change="handleVisibleChange"
  >
    <button
      ref="trigger"
      class="card-action-trigger"
      type="button"
      :title="label"
      @click.stop
    >
      <MoreHorizontal :size="19" />
    </button>
    <template #dropdown>
      <el-dropdown-menu>
        <el-dropdown-item command="edit"
          ><Pencil :size="15" /><span>{{ editLabel }}</span></el-dropdown-item
        >
        <el-dropdown-item
          command="delete"
          divided
          class="is-danger"
          ><Trash2 :size="15" /><span>{{ deleteLabel }}</span></el-dropdown-item
        >
      </el-dropdown-menu>
    </template>
  </el-dropdown>
</template>

<style scoped>
.card-action-menu {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 6;
  opacity: 1;
  pointer-events: auto;
}
.card-action-trigger {
  width: var(--control-height-md);
  height: var(--control-height-md);
  padding: 0;
  display: grid;
  place-items: center;
  color: var(--web-text-secondary);
  background: var(--web-card-menu-bg);
  border: 1px solid var(--web-line-emphasis);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition:
    color 0.18s,
    background-color 0.18s,
    border-color 0.18s;
}
.card-action-trigger:hover:not(:disabled) {
  color: var(--web-text);
  background: var(--web-hover);
  border-color: var(--web-timeline-focus-line);
}
:global(.card-action-popper.el-popper) {
  width: 112px;
  min-width: 112px;
  padding: 4px;
  background: var(--web-surface);
  border-color: var(--web-line-strong);
  box-shadow: var(--web-shadow-card-menu);
}
:global(.card-action-popper .el-popper__arrow) {
  display: none;
}
:global(.card-action-popper .el-dropdown-menu) {
  padding: 0;
  background: transparent;
}
:global(.card-action-popper .el-dropdown-menu__item) {
  min-height: 34px;
  padding: 0 10px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--web-text-secondary);
  background: transparent;
  font-size: 12px;
}
:global(.card-action-popper .el-dropdown-menu__item:hover),
:global(.card-action-popper .el-dropdown-menu__item:focus) {
  color: var(--web-accent-muted);
  background: var(--web-hover);
}
:global(.card-action-popper .el-dropdown-menu__item.is-danger) {
  color: var(--web-accent-dark);
}
:global(.card-action-popper .el-dropdown-menu__item.is-danger:hover),
:global(.card-action-popper .el-dropdown-menu__item.is-danger:focus) {
  color: var(--web-accent-dark);
  background: var(--web-hover);
}
:global(.card-action-popper .el-dropdown-menu__item--divided) {
  margin-top: 4px;
  border-top-color: var(--web-line-strong);
}
</style>
