fix: 메뉴 드래그 시각적 피드백 및 순서변경 버그 수정

- forceFallback: true 추가 (네이티브 드래그 → 자체 구현)
  → mousemove 이벤트 정상 발생, 인디케이터/하이라이트 작동
- 드래그 중 텍스트 선택 방지 (body.is-dragging + user-select: none)
- 순서변경(reorder) 시 원래 부모 유지 (계층 변경 버그 수정)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-01 22:19:53 +09:00
parent 2f73a1f4e6
commit 5e1d4380d9

View File

@@ -122,6 +122,38 @@ class="bg-white rounded-lg shadow-sm overflow-hidden">
z-index: 100;
transition: width 0.15s, left 0.15s;
}
/* SortableJS fallback 모드 스타일 (forceFallback: true) */
.sortable-fallback {
opacity: 0.9;
background: white !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
border-radius: 4px;
z-index: 9998 !important;
}
/* 드래그 중 텍스트 선택 방지 */
.sortable-drag, .sortable-chosen, .sortable-ghost {
user-select: none !important;
-webkit-user-select: none !important;
}
/* 드래그 핸들 영역 텍스트 선택 방지 */
.drag-handle {
user-select: none;
-webkit-user-select: none;
}
/* 드래그 중 전체 페이지 텍스트 선택 방지 */
body.is-dragging {
user-select: none !important;
-webkit-user-select: none !important;
cursor: grabbing !important;
}
body.is-dragging * {
user-select: none !important;
-webkit-user-select: none !important;
}
</style>
@endpush
@@ -268,9 +300,13 @@ function onDragMove(e) {
}
// SortableJS 초기화 - 노션 스타일 인덴트
// forceFallback: true → 네이티브 드래그 대신 자체 구현 사용 (mousemove 이벤트 정상 발생)
tbody.sortableInstance = new Sortable(tbody, {
handle: '.drag-handle',
animation: 150,
forceFallback: true, // 브라우저 네이티브 드래그 비활성화 → mousemove 정상 작동
fallbackClass: 'sortable-fallback',
fallbackOnBody: true,
ghostClass: 'bg-blue-50',
chosenClass: 'bg-blue-100',
dragClass: 'shadow-lg',
@@ -281,6 +317,9 @@ function onDragMove(e) {
currentDragItem = evt.item;
dragIndicator = createDragIndicator();
// 드래그 중 텍스트 선택 방지
document.body.classList.add('is-dragging');
// 마우스 이동 이벤트 리스너 추가
document.addEventListener('mousemove', onDragMove);
document.addEventListener('touchmove', onDragMove);
@@ -297,6 +336,9 @@ function onDragMove(e) {
removeDragIndicator();
currentDragItem = null;
// 드래그 중 텍스트 선택 방지 해제
document.body.classList.remove('is-dragging');
const movedItem = evt.item;
const menuId = parseInt(movedItem.dataset.menuId);
const currentDepth = parseInt(movedItem.dataset.depth) || 0;
@@ -352,17 +394,10 @@ function onDragMove(e) {
}
// 수평 이동 없음 → 같은 레벨에서 순서만 변경
else {
// 위 행과 같은 부모로 (기존 동작)
if (newIndex > 0) {
const prevRow = rows[newIndex - 1];
if (prevRow) {
const prevParentIdRaw = prevRow.dataset.parentId;
newParentId = prevParentIdRaw === '' ? null : (prevParentIdRaw ? parseInt(prevParentIdRaw) : null);
}
} else {
newParentId = null;
}
console.log('↔ REORDER: 위 행과 같은 레벨로, newParentId:', newParentId);
// 원래 부모 유지 (순서만 변경, 계층 변경 안함)
newParentId = oldPid;
action = 'reorder';
console.log('↔ REORDER: 같은 부모 유지, parentId:', newParentId);
}
const parentChanged = oldPid !== newParentId;