관리자 블록 에디터 키보드 흐름 보정
This commit is contained in:
@@ -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'"
|
||||
>
|
||||
<button
|
||||
v-for="command in visibleCommands"
|
||||
v-for="(command, commandIndex) in visibleCommands"
|
||||
:key="`${command.type}-${command.level || 'default'}`"
|
||||
class="admin-block-editor__slash-item grid w-full gap-0.5 px-4 py-3 text-left hover:bg-surface"
|
||||
:class="commandIndex === highlightedCommandIndex ? 'bg-surface' : ''"
|
||||
type="button"
|
||||
@mousedown.prevent="applyCommand(command)"
|
||||
>
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# 의사결정 이력
|
||||
|
||||
## 2026-05-01 v0.0.11
|
||||
|
||||
### 블록 에디터 키보드 흐름 보정
|
||||
|
||||
빈 문단에서 Enter를 누를 때도 다음 빈 문단 블록을 생성하도록 유지한다. 작성 중 의도적으로 여백을 두거나 다음 입력 위치로 이동하는 행동이 자연스러운 글쓰기 흐름이기 때문이다. 저장 시에는 기존처럼 비어 있는 블록을 마크다운 문자열에 포함하지 않는다.
|
||||
|
||||
슬래시 메뉴는 입력 포커스를 본문 블록에 둔 채 키보드로 선택한다. `/제목 3`처럼 필터링한 뒤 Enter를 누르면 현재 강조된 항목을 적용하고, 위/아래 방향키로 강조 항목을 이동한다. 이렇게 하면 메뉴 항목으로 실제 DOM 포커스를 옮기지 않아도 Ghost류 에디터처럼 연속 입력 흐름을 유지할 수 있다.
|
||||
|
||||
## 2026-05-01 v0.0.10
|
||||
|
||||
### 블록 에디터 입력 안정화 결정
|
||||
|
||||
@@ -205,8 +205,11 @@ components/content/
|
||||
- 저장 데이터는 기존 `content` 필드의 마크다운 문자열을 유지한다.
|
||||
- `/` 입력 시 블록 선택 메뉴를 표시한다.
|
||||
- `/` 명령 메뉴는 화면 하단 공간이 부족하면 현재 블록 위쪽으로 표시한다.
|
||||
- `/` 명령 메뉴가 열린 상태에서 Enter를 누르면 현재 강조된 메뉴 항목을 적용한다.
|
||||
- `/` 명령 메뉴가 열린 상태에서 위/아래 방향키로 강조 항목을 이동한다.
|
||||
- 블록 메뉴는 문단, 제목 2, 제목 3, 인용, 목록, 코드, 구분선을 제공한다.
|
||||
- `#`, `##`, `###`, `>`, `-` 입력 후 공백을 누르면 현재 블록 타입을 즉시 변환한다.
|
||||
- 빈 문단에서 Enter를 누르면 다음 빈 문단 블록을 생성한다.
|
||||
- 빈 블록 placeholder는 현재 활성 블록 또는 첫 빈 블록에만 표시한다.
|
||||
- 제목은 별도 라벨 영역이 아니라 에디터 상단의 큰 제목 입력으로 표시한다.
|
||||
- 관리자 작성 화면과 공개 본문은 같은 마크다운 렌더링 기준을 사용한다.
|
||||
@@ -288,6 +291,6 @@ APP_PORT=43118
|
||||
|
||||
## 버전 관리
|
||||
|
||||
- 현재 버전: v0.0.10
|
||||
- 현재 버전: v0.0.11
|
||||
- 첫 커밋 이후 변경사항을 커밋할 때마다 패치 버전 증가
|
||||
- 메이저/마이너 버전은 구조 변경 또는 기능 묶음 단위로 결정
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
## 1차 관리자 개발
|
||||
|
||||
- [ ] 이미지 업로드
|
||||
- [ ] 블록 에디터 브라우저 수동 QA: 빈 줄 Enter, `/` 메뉴 필터, 방향키, Enter 선택, 한글 조합 입력 확인
|
||||
- [ ] 블록 에디터 저장/수정 왕복 QA: 기존 글 수정 시 블록 파싱, 저장 후 다시 열기 확인
|
||||
- [ ] 이미지 업로드 블록 구현
|
||||
- [ ] 콜아웃, 토글, 임베드 블록 추가
|
||||
- [ ] 글 작성 중 자동 저장
|
||||
|
||||
## 2차 관리자 개발
|
||||
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# 업데이트 이력
|
||||
|
||||
## v0.0.11
|
||||
|
||||
- 관리자 블록 에디터에서 빈 문단 Enter 입력 시 새 빈 블록이 생성되도록 수정.
|
||||
- 관리자 블록 에디터의 `/` 명령 메뉴에서 Enter로 선택 항목을 적용하도록 수정.
|
||||
- 관리자 블록 에디터의 `/` 명령 메뉴에 위/아래 방향키 선택 이동 추가.
|
||||
- 관리자 글 에디터 후속 작업 순서 정리.
|
||||
- 패키지 버전을 0.0.11로 갱신.
|
||||
|
||||
## v0.0.10
|
||||
|
||||
- 관리자 블록 에디터의 `contenteditable` 입력 중복 문제 수정.
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "0.0.10",
|
||||
"version": "0.0.11",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sori.studio",
|
||||
"version": "0.0.10",
|
||||
"version": "0.0.11",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "0.0.10",
|
||||
"version": "0.0.11",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Reference in New Issue
Block a user