diff --git a/components/admin/AdminBlockEditor.vue b/components/admin/AdminBlockEditor.vue index 373f8e7..49ea75b 100644 --- a/components/admin/AdminBlockEditor.vue +++ b/components/admin/AdminBlockEditor.vue @@ -13,6 +13,7 @@ const blockRefs = ref([]) const activeBlockId = ref('') const slashQuery = ref('') const slashMenuDirection = ref('down') +const highlightedCommandIndex = ref(0) const isApplyingExternalValue = ref(false) let blockIdSeed = 0 @@ -368,6 +369,8 @@ const updateSlashQuery = (block) => { slashQuery.value = block.text.startsWith('/') ? block.text.slice(1).trim().toLowerCase() : '' + + highlightedCommandIndex.value = 0 } const activeBlockIndex = computed(() => editorBlocks.value.findIndex((block) => block.id === activeBlockId.value)) @@ -390,6 +393,8 @@ const visibleCommands = computed(() => { ].some((keyword) => keyword.toLowerCase().includes(slashQuery.value))) }) +const highlightedCommand = computed(() => visibleCommands.value[highlightedCommandIndex.value]) + /** * 슬래시 메뉴 명령 적용 * @param {Object} command - 블록 명령 @@ -425,6 +430,36 @@ const applyCommand = (command) => { focusBlock(index) } +/** + * 슬래시 메뉴 선택을 아래로 이동 + * @param {KeyboardEvent} event - 키보드 이벤트 + * @returns {void} + */ +const highlightNextCommand = (event) => { + if (!visibleCommands.value.length) { + return + } + + event.preventDefault() + highlightedCommandIndex.value = (highlightedCommandIndex.value + 1) % visibleCommands.value.length +} + +/** + * 슬래시 메뉴 선택을 위로 이동 + * @param {KeyboardEvent} event - 키보드 이벤트 + * @returns {void} + */ +const highlightPreviousCommand = (event) => { + if (!visibleCommands.value.length) { + return + } + + event.preventDefault() + highlightedCommandIndex.value = highlightedCommandIndex.value === 0 + ? visibleCommands.value.length - 1 + : highlightedCommandIndex.value - 1 +} + /** * 엔터 키로 다음 블록 생성 * @param {KeyboardEvent} event - 키보드 이벤트 @@ -434,16 +469,18 @@ const applyCommand = (command) => { const handleEnter = (event, index) => { const currentBlock = editorBlocks.value[index] + if (visibleCommands.value.length && currentBlock.text.startsWith('/')) { + event.preventDefault() + applyCommand(highlightedCommand.value || visibleCommands.value[0]) + return + } + if (currentBlock.type === 'code' && !event.shiftKey) { return } event.preventDefault() - if (!currentBlock.text.trim() && currentBlock.type === 'paragraph') { - return - } - if (currentBlock.type === 'divider') { editorBlocks.value.splice(index + 1, 0, createEditorBlock()) emitContent() @@ -550,6 +587,8 @@ watch(editorBlocks, () => { @focus="activateBlock(block)" @input="updateBlockText($event, index)" @keydown.enter="handleEnter($event, index)" + @keydown.down="highlightNextCommand" + @keydown.up="highlightPreviousCommand" @keydown.backspace="handleBackspace($event, index)" /> @@ -569,9 +608,10 @@ watch(editorBlocks, () => { :class="slashMenuDirection === 'up' ? 'bottom-full mb-2' : 'top-full mt-2'" >