<script setup lang="ts" generic="T extends string">
// 커스텀 드롭다운(셀렉트) — 네이티브 select 대신 디자인 토큰에 맞춘 정렬/필터 공통 UI.
// 사용처: 프로젝트 목록·업무 목록 정렬, 내 업무 프로젝트/정렬, 활동 멤버 필터.
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'

interface Option {
  value: T
  label: string
}
const props = withDefaults(
  defineProps<{
    modelValue: T
    options: Option[]
    // 메뉴 정렬 기준(트리거 좌/우 끝)
    align?: 'left' | 'right'
    // 툴바에서 오른쪽으로 밀어 붙일지(기존 margin-left:auto 대체)
    pushRight?: boolean
    // 폼 필드처럼 전체 너비로 펼칠지(라벨이 늘어나고 chevron 이 오른쪽 끝에 붙음)
    block?: boolean
    ariaLabel?: string
  }>(),
  { align: 'left', pushRight: false, block: false, ariaLabel: undefined },
)
const emit = defineEmits<{ (e: 'update:modelValue', value: T): void }>()

const open = ref(false)
const root = ref<HTMLElement | null>(null)

// 현재 선택된 옵션 라벨(트리거에 표시)
const selectedLabel = computed(
  () => props.options.find((o) => o.value === props.modelValue)?.label ?? '',
)

function toggle(): void {
  open.value = !open.value
}
function select(value: T): void {
  emit('update:modelValue', value)
  open.value = false
}

// 바깥 클릭 / Esc 로 닫기
function onDocClick(e: MouseEvent): void {
  if (open.value && root.value && !root.value.contains(e.target as Node)) {
    open.value = false
  }
}
function onKeydown(e: KeyboardEvent): void {
  if (open.value && e.key === 'Escape') open.value = false
}
onMounted(() => {
  document.addEventListener('click', onDocClick)
  document.addEventListener('keydown', onKeydown)
})
onBeforeUnmount(() => {
  document.removeEventListener('click', onDocClick)
  document.removeEventListener('keydown', onKeydown)
})
</script>

<template>
  <div
    ref="root"
    class="dd"
    :class="{ push: pushRight, block }"
  >
    <button
      type="button"
      class="dd-trigger"
      :class="{ open }"
      :aria-label="ariaLabel"
      aria-haspopup="listbox"
      :aria-expanded="open"
      @click="toggle"
    >
      <!-- 선행 아이콘(사용처별 SVG) -->
      <span
        v-if="$slots.icon"
        class="dd-ico"
      ><slot name="icon" /></span>
      <span class="dd-label">{{ selectedLabel }}</span>
      <svg
        class="dd-chev"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      ><path d="m6 9 6 6 6-6" /></svg>
    </button>

    <ul
      v-if="open"
      class="dd-menu"
      :class="align === 'right' ? 'right' : 'left'"
      role="listbox"
    >
      <li
        v-for="opt in options"
        :key="opt.value"
        class="dd-opt"
        :class="{ active: opt.value === modelValue }"
        role="option"
        :aria-selected="opt.value === modelValue"
        @click="select(opt.value)"
      >
        <span class="dd-opt-label">{{ opt.label }}</span>
        <svg
          v-if="opt.value === modelValue"
          class="dd-check"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2.4"
          stroke-linecap="round"
          stroke-linejoin="round"
        ><path d="M20 6 9 17l-5-5" /></svg>
      </li>
    </ul>
  </div>
</template>

<style scoped>
.dd {
  position: relative;
  display: inline-flex;
}
.dd.push {
  margin-left: auto;
}
/* 폼 필드용 전체 너비 변형 */
.dd.block {
  display: flex;
  width: 100%;
}
.dd.block .dd-trigger {
  width: 100%;
  max-width: none;
}
.dd.block .dd-label {
  flex: 1;
}

/* 트리거 — 기존 정렬/필터 pill 과 동일한 외형 */
.dd-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  height: 2.25rem;
  max-width: 14rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 0 0.625rem;
  background: #fff;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text-2);
  cursor: pointer;
  white-space: nowrap;
}
.dd-trigger:hover {
  background: #f9fafb;
}
.dd-trigger.open {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-weak);
}
.dd-ico {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--text-3);
}
.dd-ico :deep(svg) {
  width: 0.9375rem;
  height: 0.9375rem;
}
.dd-label {
  overflow: hidden;
  text-overflow: ellipsis;
}
.dd-chev {
  width: 0.875rem;
  height: 0.875rem;
  flex-shrink: 0;
  color: var(--text-3);
  transition: transform 0.15s ease;
}
.dd-trigger.open .dd-chev {
  transform: rotate(180deg);
}

/* 메뉴 */
.dd-menu {
  position: absolute;
  top: calc(100% + 0.375rem);
  z-index: 60;
  min-width: 100%;
  margin: 0;
  padding: 0.25rem;
  list-style: none;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(20, 24, 33, 0.14);
}
.dd-menu.left {
  left: 0;
}
.dd-menu.right {
  right: 0;
}
.dd-opt {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.4375rem 0.625rem;
  border-radius: var(--radius-sm);
  font-size: 0.8125rem;
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
}
.dd-opt:hover {
  background: #f1f2f4;
}
.dd-opt.active {
  color: var(--accent);
  font-weight: 600;
  background: var(--accent-weak);
}
.dd-opt-label {
  flex: 1;
}
.dd-check {
  width: 0.875rem;
  height: 0.875rem;
  flex-shrink: 0;
}
</style>
