fix(B06): 추가 기슭막이 선택 시 부모 칸의 단 수·옵션 유지

단 수·옵션 행은 조정창 사본에서 DOM째 빌려온 행이라, 다단 벽을 고르면
clearSideSlots()가 되돌린 뒤 다시 채우지 못해 유입구·유출구 칸이 비었다.
다단 벽에서도 tierChannel을 부모 채널로 열어 두 행을 살리고(revetWall에
isExtra 포함), 이식은 부모 칸으로 보낸다. 추가 기슭막이 칸에는 형태·
높이·구간값만 남긴다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-30 00:32:09 +09:00
co-authored by Claude Opus 5
parent c8b5a8bf38
commit c8ce7ae02a
2 changed files with 35 additions and 18 deletions
+24 -16
View File
@@ -30,13 +30,12 @@ export function setAdjustSideSlots(next: SideSlots): void {
sideSlots = next;
}
/** 다단 벽에서 옮겨 오는 행 — 폼에 자기 칸이 없어 형태·높이·구간값까지 전부 온다. */
/** 다단 벽에서 옮겨 오는 행 — 폼에 자기 칸이 없어 형태·높이·구간값이 온다.
* 단 수·옵션은 부모 벽의 값이라 여기 오지 않고 부모 칸에 남는다(2026-08-30 사용자). */
const EXTRA_ROWS = [
".b06-structure-panel__struct--form",
".b06-structure-panel__struct--height",
".b06-structure-panel__struct--span",
".b06-structure-panel__struct--tier",
".b06-structure-panel__struct--options",
].join(", ");
/** 유입구·유출구는 폼이 이미 가진 항목 말고 단 수·옵션만 받는다. */
@@ -97,6 +96,18 @@ function clearSideSlots(): void {
}
}
/** 행 DOM을 제자리 표식과 함께 옮긴다 — 숨은 행은 그대로 둔다. */
function moveRows(panelRoot: HTMLElement, selector: string, target: HTMLElement): void {
panelRoot.querySelectorAll<HTMLElement>(selector).forEach((row) => {
if (row.classList.contains("is-hidden")) return;
// 제자리에 표식을 남기고 옮긴다 — 선택이 바뀌면 이 자리로 되돌린다.
const anchor = document.createComment("adjust-row");
row.parentNode?.insertBefore(anchor, row);
movedRows.push({ row, anchor });
target.append(row);
});
}
/**
* 조정창 사본의 단 수·옵션 행을 고른 벽 쪽(유입/유출) 그룹으로 옮긴다. 행 DOM을
* 그대로 옮기므로 배선·값 동기는 손대지 않는다. 자리가 없으면 사본에 그대로 둔다.
@@ -111,21 +122,18 @@ function relocateSideRows(panelRoot: HTMLElement): void {
if (side === "extra") {
const group = target.closest<HTMLElement>("fieldset");
if (!group) return;
const owner = slotOf(panelRoot.dataset.extraSide === "inlet" ? "inlet" : "outlet").closest(
"fieldset",
);
const ownerSlot = slotOf(panelRoot.dataset.extraSide === "inlet" ? "inlet" : "outlet");
const owner = ownerSlot.closest("fieldset");
if (owner && owner !== group) owner.after(group);
group.hidden = false;
} else if (!slotUsable(target)) return;
const rows = panelRoot.querySelectorAll<HTMLElement>(side === "extra" ? EXTRA_ROWS : SIDE_ROWS);
rows.forEach((row) => {
if (row.classList.contains("is-hidden")) return;
// 제자리에 표식을 남기고 옮긴다 — 선택이 바뀌면 이 자리로 되돌린다.
const anchor = document.createComment("adjust-row");
row.parentNode?.insertBefore(anchor, row);
movedRows.push({ row, anchor });
target.append(row);
});
moveRows(panelRoot, EXTRA_ROWS, target);
// 단 수·옵션은 그 단이 딸린 부모 벽의 값이라 부모 칸에 그대로 세운다 — 다단을
// 고를 때 그 두 행이 사라지던 것을 막는다(2026-08-30 사용자).
if (slotUsable(ownerSlot)) moveRows(panelRoot, SIDE_ROWS, ownerSlot);
} else {
if (!slotUsable(target)) return;
moveRows(panelRoot, SIDE_ROWS, target);
}
// 고른 구조물이 어느 쪽인지 그룹 테두리로 알린다(2026-08-29 사용자 지시 3).
const group = target.closest<HTMLElement>("fieldset");
if (!group) return;
@@ -569,7 +569,15 @@ export function buildStructurePanel(
// 유출 벽과 독립 기슭막이 유입 벽에서는 **항상** 보인다(2026-08-22 사용자 ⑤ —
// 상태 따라 나타났다 사라지면 레이아웃이 널뛴다). 집수정 계류측은 둘 자리가
// 있을 때만 뜬다(배관 유입 기슭막이는 다단 채널 자체가 없다).
tierChannel = key === "outlet" ? "outlet" : key === "inlet" ? "basin" : null;
// 다단 벽을 고른 동안에도 단 수는 **그 단이 딸린 부모 벽**의 값이다 — 채널을
// 부모 쪽으로 열어 두고 행은 부모 칸에 남긴다(2026-08-30 사용자: 추가 기슭막이를
// 고르면 단 수·옵션이 사라지던 것).
tierChannel =
key === "outlet" || key.startsWith("extra")
? "outlet"
: key === "inlet" || key.startsWith("bextra")
? "basin"
: null;
// 좌측 [횡단 조정] 사본의 단 수·옵션 행이 어느 그룹(유입구·유출구)으로 옮겨
// 붙을지 알린다(2026-08-29 지시 4). 다단 벽은 자기 채널 쪽으로 간다.
// 다단(추가) 벽은 유입·유출 어느 칸에도 자기 자리가 없다 — 유입구·유출구 아래
@@ -596,7 +604,8 @@ export function buildStructurePanel(
// 기슭막이 기준벽(배관 유입·유출, 독립 좌·우)에서는 **항상** 보인다(2026-08-30
// 사용자). 링크 카드에서 만져도 제어기가 소유 측점 값을 고치므로 안전하다.
// 집수정 유입은 계류측 단이 설 자리가 있을 때만 — 벽이 아니라 구체다.
const revetWall = (key === "inlet" || key === "outlet") && !isBasin;
// 다단 벽도 포함한다 — 행은 부모 칸에 서 있고, 그 값(부모 단 수·옵션)을 그대로 쓴다.
const revetWall = ((key === "inlet" || key === "outlet") && !isBasin) || isExtra;
extraRow.classList.toggle(
"is-hidden",
!revetWall && !(isBasin && tierChannel === "basin" && (tiers.canAdd || tiers.count > 0)),