에디터 슬래시 메뉴 컴포넌트 분리
This commit is contained in:
71
components/admin/AdminMarkdownSlashMenu.vue
Normal file
71
components/admin/AdminMarkdownSlashMenu.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user