블록 메뉴와 드래그 이동 안정화
This commit is contained in:
@@ -1236,6 +1236,46 @@ const updateBlockDropTarget = (event, targetIndex) => {
|
||||
dragTargetPosition.value = event.clientY < rect.top + rect.height / 2 ? 'before' : 'after'
|
||||
}
|
||||
|
||||
/**
|
||||
* 블록 드래그 상태 초기화
|
||||
* @returns {void}
|
||||
*/
|
||||
const clearBlockDragState = () => {
|
||||
draggingBlockId.value = ''
|
||||
dragTargetIndex.value = -1
|
||||
dragTargetPosition.value = ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 드롭 위치 기준으로 블록 이동
|
||||
* @param {string} draggedId - 이동할 블록 ID
|
||||
* @param {number} targetIndex - 이동 대상 인덱스
|
||||
* @param {string} targetPosition - 이동 위치
|
||||
* @returns {boolean} 이동 여부
|
||||
*/
|
||||
const moveDraggedBlock = (draggedId, targetIndex, targetPosition) => {
|
||||
const sourceIndex = getBlockIndex(draggedId)
|
||||
const insertionIndex = targetPosition === 'after' ? targetIndex + 1 : targetIndex
|
||||
|
||||
if (sourceIndex < 0 || targetIndex < 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
const nextTargetIndex = sourceIndex < insertionIndex ? insertionIndex - 1 : insertionIndex
|
||||
|
||||
if (sourceIndex === nextTargetIndex) {
|
||||
return false
|
||||
}
|
||||
|
||||
const [draggedBlock] = editorBlocks.value.splice(sourceIndex, 1)
|
||||
editorBlocks.value.splice(nextTargetIndex, 0, draggedBlock)
|
||||
selectedBlockId.value = draggedBlock.id
|
||||
normalizeTrailingTextBlock()
|
||||
emitContent()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 블록 드롭 이동 처리
|
||||
* @param {DragEvent} event - 드롭 이벤트
|
||||
@@ -1245,26 +1285,10 @@ const updateBlockDropTarget = (event, targetIndex) => {
|
||||
const dropBlock = (event, targetIndex) => {
|
||||
event.preventDefault()
|
||||
const draggedId = event.dataTransfer.getData('text/plain') || draggingBlockId.value
|
||||
const sourceIndex = getBlockIndex(draggedId)
|
||||
const targetPosition = dragTargetIndex.value === targetIndex ? dragTargetPosition.value : 'after'
|
||||
const insertionIndex = targetPosition === 'after' ? targetIndex + 1 : targetIndex
|
||||
|
||||
if (sourceIndex < 0 || targetIndex < 0) {
|
||||
draggingBlockId.value = ''
|
||||
dragTargetIndex.value = -1
|
||||
dragTargetPosition.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
const [draggedBlock] = editorBlocks.value.splice(sourceIndex, 1)
|
||||
const nextTargetIndex = sourceIndex < insertionIndex ? insertionIndex - 1 : insertionIndex
|
||||
editorBlocks.value.splice(nextTargetIndex, 0, draggedBlock)
|
||||
draggingBlockId.value = ''
|
||||
dragTargetIndex.value = -1
|
||||
dragTargetPosition.value = ''
|
||||
selectedBlockId.value = draggedBlock.id
|
||||
normalizeTrailingTextBlock()
|
||||
emitContent()
|
||||
moveDraggedBlock(draggedId, targetIndex, targetPosition)
|
||||
clearBlockDragState()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1272,9 +1296,11 @@ const dropBlock = (event, targetIndex) => {
|
||||
* @returns {void}
|
||||
*/
|
||||
const finishBlockDrag = () => {
|
||||
draggingBlockId.value = ''
|
||||
dragTargetIndex.value = -1
|
||||
dragTargetPosition.value = ''
|
||||
if (draggingBlockId.value && dragTargetIndex.value >= 0 && dragTargetPosition.value) {
|
||||
moveDraggedBlock(draggingBlockId.value, dragTargetIndex.value, dragTargetPosition.value)
|
||||
}
|
||||
|
||||
clearBlockDragState()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1350,6 +1376,7 @@ defineExpose({
|
||||
'admin-block-editor__row--dragging opacity-50': draggingBlockId === block.id,
|
||||
'admin-block-editor__row--drop-before': dragTargetIndex === index && dragTargetPosition === 'before',
|
||||
'admin-block-editor__row--drop-after': dragTargetIndex === index && dragTargetPosition === 'after',
|
||||
'admin-block-editor__row--menu-open z-30': visibleCommands.length && activeBlockId === block.id,
|
||||
'admin-block-editor__row--text': isTextBlock(block),
|
||||
'admin-block-editor__row--structure': !isTextBlock(block)
|
||||
}"
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# 의사결정 이력
|
||||
|
||||
## 2026-05-07 v0.0.41
|
||||
|
||||
### 명령 메뉴 계층과 개발 도구 표시 결정
|
||||
|
||||
관리자 블록 에디터의 `/` 명령 메뉴가 열린 행은 다른 블록 행보다 위 stacking 순서로 올린다. 메뉴가 절대 위치로 열릴 때 아래 블록의 텍스트가 같은 레이어에 남아 있으면 메뉴 배경 위로 겹쳐 보일 수 있기 때문이다.
|
||||
|
||||
블록 이동은 `drop` 이벤트뿐 아니라 `dragend`에서도 현재 삽입선 위치를 기준으로 확정한다. 브라우저와 입력 요소 조합에 따라 contenteditable 주변에서 `drop` 이벤트가 안정적으로 들어오지 않을 수 있으므로, 사용자가 본 삽입선과 실제 결과가 어긋나지 않게 하기 위해서다.
|
||||
|
||||
개발 서버의 Nuxt DevTools는 현재 관리자 글쓰기 전체 화면 QA를 방해하므로 기본 비활성화한다. 하단 검은 도킹 패널은 애플리케이션 UI가 아니라 개발 도구 영역이지만, 편집 화면 높이와 스크롤 문제를 확인할 때 혼동을 만들 수 있기 때문이다.
|
||||
|
||||
## 2026-05-07 v0.0.40
|
||||
|
||||
### 글쓰기 스크롤과 드래그 드롭 피드백 결정
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
| 파일 | 기능 |
|
||||
|------|------|
|
||||
| package.json | Nuxt 실행 스크립트와 의존성 |
|
||||
| nuxt.config.js | Nuxt 앱 설정 |
|
||||
| nuxt.config.js | Nuxt 앱 설정, Tailwind 모듈 연결, 관리자 QA를 위한 개발 도구 비활성화 |
|
||||
| tailwind.config.js | Tailwind 테마 설정 |
|
||||
| assets/css/main.css | 전역 스타일 |
|
||||
| composables/useMenuState.js | 좌측 메뉴 열림 상태 관리 |
|
||||
|
||||
@@ -277,6 +277,7 @@ components/content/
|
||||
- 저장 데이터는 기존 `content` 필드의 마크다운 문자열을 유지한다.
|
||||
- `/` 입력 시 블록 선택 메뉴를 표시한다.
|
||||
- `/` 명령 메뉴는 화면 하단 공간이 부족하면 현재 블록 위쪽으로 표시한다.
|
||||
- `/` 명령 메뉴가 열린 블록 행은 아래 블록보다 위 stacking 순서로 표시해 메뉴와 본문 텍스트가 겹쳐 보이지 않게 한다.
|
||||
- `/` 명령 메뉴가 열린 상태에서 Enter를 누르면 현재 강조된 메뉴 항목을 적용한다.
|
||||
- `/` 명령 메뉴가 열린 상태에서 위/아래 방향키로 강조 항목을 이동한다.
|
||||
- `/` 명령 메뉴 필터는 한글 조합 입력 완료와 방향키/Enter 입력 직전에 현재 DOM 텍스트를 기준으로 동기화한다.
|
||||
@@ -292,7 +293,7 @@ components/content/
|
||||
- 블록 왼쪽 핸들은 hover/focus 상태에서 AFFiNE 참고 스타일의 세로 막대로 표시되며, hover 시 해당 블록 높이만큼 확장해 선택 범위를 드러낸다.
|
||||
- 블록 왼쪽 핸들을 클릭하면 블록을 선택하고 Delete 또는 Backspace로 해당 블록을 삭제할 수 있다.
|
||||
- 블록 왼쪽 핸들을 드래그하면 블록 순서를 이동할 수 있다.
|
||||
- 블록 드래그 중에는 현재 포인터 위치 기준으로 대상 블록 위 또는 아래에 삽입선을 표시하고, 드롭 시 표시 위치와 같은 곳으로 이동한다.
|
||||
- 블록 드래그 중에는 현재 포인터 위치 기준으로 대상 블록 위 또는 아래에 삽입선을 표시하고, 드롭 또는 드래그 종료 시 표시 위치와 같은 곳으로 이동한다.
|
||||
- 빈 블록 placeholder는 현재 활성 블록 또는 첫 빈 블록에만 표시한다.
|
||||
- 에디터 마지막에는 클릭 가능한 빈 문단 블록을 항상 유지하며, 해당 블록이 비어 있으면 저장 콘텐츠에는 포함하지 않는다.
|
||||
- 제목은 별도 라벨 영역이 아니라 에디터 상단의 큰 제목 입력으로 표시한다.
|
||||
@@ -451,6 +452,6 @@ APP_PORT=43118
|
||||
|
||||
## 버전 관리
|
||||
|
||||
- 현재 버전: v0.0.40
|
||||
- 현재 버전: v0.0.41
|
||||
- 첫 커밋 이후 변경사항을 커밋할 때마다 패치 버전 증가
|
||||
- 메이저/마이너 버전은 구조 변경 또는 기능 묶음 단위로 결정
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# 업데이트 이력
|
||||
|
||||
## v0.0.41
|
||||
|
||||
- 관리자 블록 에디터 `/` 명령 메뉴가 아래 블록 텍스트와 겹쳐 보이던 문제 수정.
|
||||
- 관리자 블록 에디터 드래그 종료 시 삽입선 위치 기준으로 블록 이동이 확정되도록 보정.
|
||||
- 개발 서버에서 Nuxt DevTools 도킹 패널로 보이는 하단 검은 영역을 막기 위해 DevTools 비활성화.
|
||||
- 기술 명세 현재 버전을 v0.0.41로 갱신.
|
||||
- 패키지 버전을 0.0.41로 갱신.
|
||||
|
||||
## v0.0.40
|
||||
|
||||
- 관리자 글쓰기 화면에서 바깥 문서가 함께 스크롤되어 하단 배경이 노출되던 문제 수정.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: '2026-04-29',
|
||||
devtools: {
|
||||
enabled: false
|
||||
},
|
||||
modules: ['@nuxtjs/tailwindcss'],
|
||||
components: [
|
||||
{
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "0.0.40",
|
||||
"version": "0.0.41",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sori.studio",
|
||||
"version": "0.0.40",
|
||||
"version": "0.0.41",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "0.0.40",
|
||||
"version": "0.0.41",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Reference in New Issue
Block a user