From 5c9778c018459935ad753357141e733e1cb8dbc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Wed, 11 Feb 2026 09:11:30 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=EB=A9=94=EB=89=B4=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EB=93=9C=EB=9E=98=EA=B7=B8=20UX=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20-=20=EC=B2=B4=ED=81=AC=EB=B0=95=EC=8A=A4=20?= =?UTF-8?q?=EB=8C=80=EC=8B=A0=20=EC=83=81=EC=9C=84=20=EB=A9=94=EB=89=B4=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=20=EA=B0=90=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- public/js/menu-sortable.js | 27 +++++++++++++++------------ resources/views/menus/index.blade.php | 4 ++-- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/public/js/menu-sortable.js b/public/js/menu-sortable.js index 290c1370..0c992873 100644 --- a/public/js/menu-sortable.js +++ b/public/js/menu-sortable.js @@ -1,7 +1,7 @@ /** * 메뉴 관리 페이지 전용 SortableJS 초기화 스크립트 * HTMX hx-boost 네비게이션에서도 동작하도록 전역으로 정의 - * - 그룹 드래그: 체크된 항목들을 함께 이동 + * - 그룹 드래그: 상위 메뉴 드래그 시 하위 메뉴 자동 포함 */ // 드래그 상태 관리 @@ -80,23 +80,26 @@ function removeDragIndicator() { } /** - * 체크된 항목 + 그 하위 메뉴를 재귀적으로 수집 + * 상위 메뉴 드래그 시 하위 메뉴를 재귀적으로 수집 + * - 자식이 있는 메뉴(상위 메뉴)를 드래그하면 자동으로 모든 하위 포함 + * - 자식이 없는 메뉴(리프 메뉴)는 단독 드래그 */ function collectGroupItems(draggedRow) { const draggedId = draggedRow.dataset.menuId; - const checkbox = draggedRow.querySelector('.menu-checkbox'); - // 드래그 항목이 체크 안 되어 있으면 단독 드래그 - if (!checkbox || !checkbox.checked) return [draggedRow]; + // 하위 메뉴가 있는지 확인 (DOM에서 data-parent-id로 탐색) + const directChildren = document.querySelectorAll(`#menu-sortable tr.menu-row[data-parent-id="${draggedId}"]`); + if (directChildren.length === 0) return [draggedRow]; - // 체크된 모든 항목 수집 - const checkedRows = Array.from(document.querySelectorAll('#menu-sortable .menu-checkbox:checked')) - .map(cb => cb.closest('tr.menu-row')) - .filter(Boolean); + // 드래그 항목 + 모든 하위 메뉴를 재귀적으로 수집 + const items = [draggedRow]; + const descendantIds = getDescendantIds(draggedId); + descendantIds.forEach(id => { + const row = document.querySelector(`#menu-sortable tr.menu-row[data-menu-id="${id}"]`); + if (row) items.push(row); + }); - if (checkedRows.length <= 1) return [draggedRow]; - - return checkedRows; + return items; } /** diff --git a/resources/views/menus/index.blade.php b/resources/views/menus/index.blade.php index 4e60e15a..ff04d114 100644 --- a/resources/views/menus/index.blade.php +++ b/resources/views/menus/index.blade.php @@ -8,7 +8,7 @@

메뉴 관리

@@ -557,7 +557,7 @@ function initFilterForm() { importFilter.classList.add('hidden'); // 설명 복원 - modeDescription.innerHTML = '드래그: 순서 변경 | → 오른쪽: 하위로 이동 | ← 왼쪽: 상위로 이동'; + modeDescription.innerHTML = '드래그: 순서 변경 | → 오른쪽: 하위로 이동 | ← 왼쪽: 상위로 이동 | 상위 메뉴 드래그 시 하위 메뉴 자동 포함'; } // 테이블 새로고침