72 lines
2.4 KiB
Vue
72 lines
2.4 KiB
Vue
<script setup>
|
|
import AdminSlashCommandIcon from './AdminSlashCommandIcon.vue'
|
|
|
|
defineProps({
|
|
/** @type {import('vue').PropType<Array<{ id: string, label: string }>>} */
|
|
commands: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
highlightedIndex: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
/** @type {import('vue').PropType<{ top: number, left: number }>} */
|
|
position: {
|
|
type: Object,
|
|
default: () => ({ top: 0, left: 0 })
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['highlight', 'select'])
|
|
const rootRef = ref(null)
|
|
|
|
/**
|
|
* 부모 컴포넌트의 위치 보정·스크롤 계산에 사용할 메뉴 루트 엘리먼트를 반환한다.
|
|
* @returns {HTMLElement|null} 메뉴 루트 엘리먼트
|
|
*/
|
|
const getRootElement = () => rootRef.value
|
|
|
|
defineExpose({
|
|
getRootElement,
|
|
rootRef
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
ref="rootRef"
|
|
class="admin-markdown-editor__slash-menu fixed z-[60] w-72 max-h-[min(52vh,360px)] overflow-y-auto rounded-lg border border-[#e3e6e8] bg-white p-1 shadow-lg"
|
|
:style="{ top: `${position.top}px`, left: `${position.left}px` }"
|
|
role="listbox"
|
|
aria-label="슬래시 명령"
|
|
>
|
|
<button
|
|
v-for="(command, commandIndex) in commands"
|
|
:key="`live-slash-${command.id}`"
|
|
class="admin-markdown-editor__slash-item flex w-full items-center justify-between gap-3 rounded-md px-2.5 py-2 text-left transition-colors"
|
|
:class="commandIndex === highlightedIndex ? 'admin-markdown-editor__slash-item--active bg-[#f4f5f6]' : ''"
|
|
type="button"
|
|
role="option"
|
|
:aria-selected="commandIndex === highlightedIndex"
|
|
@mouseenter="emit('highlight', commandIndex)"
|
|
@mousedown.prevent="emit('select', command)"
|
|
>
|
|
<span class="admin-markdown-editor__slash-leading flex min-w-0 items-center gap-2.5">
|
|
<AdminSlashCommandIcon
|
|
:command-id="command.id"
|
|
class="admin-markdown-editor__slash-icon shrink-0 text-[#394047]"
|
|
/>
|
|
<span class="admin-markdown-editor__slash-label truncate text-sm text-[#15171a]">{{ command.label }}</span>
|
|
</span>
|
|
<span
|
|
class="admin-markdown-editor__slash-slug shrink-0 text-xs text-[#8b959e] transition-opacity"
|
|
:class="commandIndex === highlightedIndex ? 'opacity-100' : 'opacity-0'"
|
|
>/{{ command.id }}</span>
|
|
</button>
|
|
<p v-if="!commands.length" class="admin-markdown-editor__slash-empty px-4 py-3 text-sm text-[#6b7280]">
|
|
일치하는 명령이 없습니다.
|
|
</p>
|
|
</div>
|
|
</template>
|