블록 메뉴와 드래그 이동 안정화
This commit is contained in:
@@ -1236,6 +1236,46 @@ const updateBlockDropTarget = (event, targetIndex) => {
|
|||||||
dragTargetPosition.value = event.clientY < rect.top + rect.height / 2 ? 'before' : 'after'
|
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 - 드롭 이벤트
|
* @param {DragEvent} event - 드롭 이벤트
|
||||||
@@ -1245,26 +1285,10 @@ const updateBlockDropTarget = (event, targetIndex) => {
|
|||||||
const dropBlock = (event, targetIndex) => {
|
const dropBlock = (event, targetIndex) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const draggedId = event.dataTransfer.getData('text/plain') || draggingBlockId.value
|
const draggedId = event.dataTransfer.getData('text/plain') || draggingBlockId.value
|
||||||
const sourceIndex = getBlockIndex(draggedId)
|
|
||||||
const targetPosition = dragTargetIndex.value === targetIndex ? dragTargetPosition.value : 'after'
|
const targetPosition = dragTargetIndex.value === targetIndex ? dragTargetPosition.value : 'after'
|
||||||
const insertionIndex = targetPosition === 'after' ? targetIndex + 1 : targetIndex
|
|
||||||
|
|
||||||
if (sourceIndex < 0 || targetIndex < 0) {
|
moveDraggedBlock(draggedId, targetIndex, targetPosition)
|
||||||
draggingBlockId.value = ''
|
clearBlockDragState()
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1272,9 +1296,11 @@ const dropBlock = (event, targetIndex) => {
|
|||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
const finishBlockDrag = () => {
|
const finishBlockDrag = () => {
|
||||||
draggingBlockId.value = ''
|
if (draggingBlockId.value && dragTargetIndex.value >= 0 && dragTargetPosition.value) {
|
||||||
dragTargetIndex.value = -1
|
moveDraggedBlock(draggingBlockId.value, dragTargetIndex.value, dragTargetPosition.value)
|
||||||
dragTargetPosition.value = ''
|
}
|
||||||
|
|
||||||
|
clearBlockDragState()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1350,6 +1376,7 @@ defineExpose({
|
|||||||
'admin-block-editor__row--dragging opacity-50': draggingBlockId === block.id,
|
'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-before': dragTargetIndex === index && dragTargetPosition === 'before',
|
||||||
'admin-block-editor__row--drop-after': dragTargetIndex === index && dragTargetPosition === 'after',
|
'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--text': isTextBlock(block),
|
||||||
'admin-block-editor__row--structure': !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
|
## 2026-05-07 v0.0.40
|
||||||
|
|
||||||
### 글쓰기 스크롤과 드래그 드롭 피드백 결정
|
### 글쓰기 스크롤과 드래그 드롭 피드백 결정
|
||||||
|
|||||||
@@ -156,7 +156,7 @@
|
|||||||
| 파일 | 기능 |
|
| 파일 | 기능 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| package.json | Nuxt 실행 스크립트와 의존성 |
|
| package.json | Nuxt 실행 스크립트와 의존성 |
|
||||||
| nuxt.config.js | Nuxt 앱 설정 |
|
| nuxt.config.js | Nuxt 앱 설정, Tailwind 모듈 연결, 관리자 QA를 위한 개발 도구 비활성화 |
|
||||||
| tailwind.config.js | Tailwind 테마 설정 |
|
| tailwind.config.js | Tailwind 테마 설정 |
|
||||||
| assets/css/main.css | 전역 스타일 |
|
| assets/css/main.css | 전역 스타일 |
|
||||||
| composables/useMenuState.js | 좌측 메뉴 열림 상태 관리 |
|
| composables/useMenuState.js | 좌측 메뉴 열림 상태 관리 |
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ components/content/
|
|||||||
- 저장 데이터는 기존 `content` 필드의 마크다운 문자열을 유지한다.
|
- 저장 데이터는 기존 `content` 필드의 마크다운 문자열을 유지한다.
|
||||||
- `/` 입력 시 블록 선택 메뉴를 표시한다.
|
- `/` 입력 시 블록 선택 메뉴를 표시한다.
|
||||||
- `/` 명령 메뉴는 화면 하단 공간이 부족하면 현재 블록 위쪽으로 표시한다.
|
- `/` 명령 메뉴는 화면 하단 공간이 부족하면 현재 블록 위쪽으로 표시한다.
|
||||||
|
- `/` 명령 메뉴가 열린 블록 행은 아래 블록보다 위 stacking 순서로 표시해 메뉴와 본문 텍스트가 겹쳐 보이지 않게 한다.
|
||||||
- `/` 명령 메뉴가 열린 상태에서 Enter를 누르면 현재 강조된 메뉴 항목을 적용한다.
|
- `/` 명령 메뉴가 열린 상태에서 Enter를 누르면 현재 강조된 메뉴 항목을 적용한다.
|
||||||
- `/` 명령 메뉴가 열린 상태에서 위/아래 방향키로 강조 항목을 이동한다.
|
- `/` 명령 메뉴가 열린 상태에서 위/아래 방향키로 강조 항목을 이동한다.
|
||||||
- `/` 명령 메뉴 필터는 한글 조합 입력 완료와 방향키/Enter 입력 직전에 현재 DOM 텍스트를 기준으로 동기화한다.
|
- `/` 명령 메뉴 필터는 한글 조합 입력 완료와 방향키/Enter 입력 직전에 현재 DOM 텍스트를 기준으로 동기화한다.
|
||||||
@@ -292,7 +293,7 @@ components/content/
|
|||||||
- 블록 왼쪽 핸들은 hover/focus 상태에서 AFFiNE 참고 스타일의 세로 막대로 표시되며, hover 시 해당 블록 높이만큼 확장해 선택 범위를 드러낸다.
|
- 블록 왼쪽 핸들은 hover/focus 상태에서 AFFiNE 참고 스타일의 세로 막대로 표시되며, hover 시 해당 블록 높이만큼 확장해 선택 범위를 드러낸다.
|
||||||
- 블록 왼쪽 핸들을 클릭하면 블록을 선택하고 Delete 또는 Backspace로 해당 블록을 삭제할 수 있다.
|
- 블록 왼쪽 핸들을 클릭하면 블록을 선택하고 Delete 또는 Backspace로 해당 블록을 삭제할 수 있다.
|
||||||
- 블록 왼쪽 핸들을 드래그하면 블록 순서를 이동할 수 있다.
|
- 블록 왼쪽 핸들을 드래그하면 블록 순서를 이동할 수 있다.
|
||||||
- 블록 드래그 중에는 현재 포인터 위치 기준으로 대상 블록 위 또는 아래에 삽입선을 표시하고, 드롭 시 표시 위치와 같은 곳으로 이동한다.
|
- 블록 드래그 중에는 현재 포인터 위치 기준으로 대상 블록 위 또는 아래에 삽입선을 표시하고, 드롭 또는 드래그 종료 시 표시 위치와 같은 곳으로 이동한다.
|
||||||
- 빈 블록 placeholder는 현재 활성 블록 또는 첫 빈 블록에만 표시한다.
|
- 빈 블록 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
|
## v0.0.40
|
||||||
|
|
||||||
- 관리자 글쓰기 화면에서 바깥 문서가 함께 스크롤되어 하단 배경이 노출되던 문제 수정.
|
- 관리자 글쓰기 화면에서 바깥 문서가 함께 스크롤되어 하단 배경이 노출되던 문제 수정.
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
compatibilityDate: '2026-04-29',
|
compatibilityDate: '2026-04-29',
|
||||||
|
devtools: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
modules: ['@nuxtjs/tailwindcss'],
|
modules: ['@nuxtjs/tailwindcss'],
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "sori.studio",
|
"name": "sori.studio",
|
||||||
"version": "0.0.40",
|
"version": "0.0.41",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "sori.studio",
|
"name": "sori.studio",
|
||||||
"version": "0.0.40",
|
"version": "0.0.41",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sori.studio",
|
"name": "sori.studio",
|
||||||
"version": "0.0.40",
|
"version": "0.0.41",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Reference in New Issue
Block a user