Files
sori.studio/components/admin/AdminMarkdownSlashMenu.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>