fix(M02): 좌측 두 컨테이너 펼친 채 진입 · 끌기 뒤 놓을 자리 강조 지움 (PLAN 31-5)

- 「일위대가 조합」 · 「구조물 조합」 둘 다 펼친 채 들어옴
- 끌어 놓은 뒤 나무 칸 자신(맨 위 자리) 강조가 남던 흠 — 놓음 · 끝 · 칸 밖 나감에서 모두 지움(칸 안 자식 위로는 그대로)
- 끌기 코드를 TreePanel_Drag 로 나눔(700줄)
- 시험 한 건

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-30 20:27:02 +09:00
co-authored by Claude Opus 5.5
parent b981911755
commit fb56d7c294
4 changed files with 101 additions and 42 deletions
@@ -36,6 +36,7 @@ import {
type TreeView,
} from "./M02_MasterTemplete_Api_Fetch";
import { askFolder, askNewItem, askText } from "./M02_MasterTemplete_TreePanel_Dialogs";
import { attachTreeDrag } from "./M02_MasterTemplete_TreePanel_Drag";
import {
clipOf,
keepClip,
@@ -286,49 +287,18 @@ export function createTreePanel(opt: TreePanelOptions): TreePanelHandle {
},
{ capture: true },
);
// 끌어다 옮기기(30-2) — 쓸 수 있는 층만 · 고른 것 안의 줄을 끌면 고른 것 전부
if (opt.canEdit) {
button.draggable = true;
button.addEventListener("dragstart", (event) => {
const own = targets.get(one.id);
dragging = picked.size > 1 && picked.has(one.id) ? pickedTargets() : own ? [own] : null;
event.dataTransfer?.setData("text/plain", one.id);
if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
});
button.addEventListener("dragend", () => {
dragging = null;
dropMark(null);
});
}
if (opt.canEdit) drag.arm(button, one.id); // 끌어다 옮기기(30-2) — 쓸 수 있는 층만
return button;
};
/* --- 끌어다 놓기: 폴더 줄 위 = 그 안 · 빈 자리 = 맨 위 · 제 안으로는 안 됨 --- */
let dragging: Target[] | null = null;
const dropDest = (event: DragEvent): string[] | null => {
const row = (event.target as HTMLElement).closest<HTMLElement>("[data-node]");
const target = row ? targets.get(row.dataset.node ?? "") : undefined;
const dest = !row ? [] : target?.kind === "folder" ? target.path : null;
const blocked = dragging?.some((t) => t.kind === "folder" && dest && isUnder(dest, t.path));
return dest && !blocked ? dest : null;
};
const dropMark = (row: HTMLElement | null): void => {
body.querySelectorAll(".is-drop").forEach((b) => b.classList.remove("is-drop"));
row?.classList.add("is-drop");
};
body.addEventListener("dragover", (event) => {
if (!dragging || !dropDest(event)) return;
event.preventDefault();
dropMark((event.target as HTMLElement).closest<HTMLElement>("[data-node]") ?? body);
});
body.addEventListener("drop", (event) => {
const dest = dragging && dropDest(event);
const list = dragging;
dragging = null;
dropMark(null);
if (!dest || !list) return;
event.preventDefault();
void dropMove(list, dest);
const drag = attachTreeDrag({
body,
targetOf: (id) => targets.get(id),
listFor: (id) => {
const own = targets.get(id);
return picked.size > 1 && picked.has(id) ? pickedTargets() : own ? [own] : null;
},
move: (list, dest) => void dropMove(list, dest),
});
/** 놓은 자리로 옮김 — 이미 그 자리인 것은 뺌 · 하나면 한 개 옮기기 · 여럿이면 한 번에 */