Merge remote-tracking branch 'origin/dev' into sub_laptop_1

This commit is contained in:
2026-10-06 13:32:10 +09:00
5 changed files with 75 additions and 15 deletions
+18 -11
View File
@@ -57,11 +57,19 @@ const SIDE_ROWS = [
".b06-structure-panel__struct--wall-height",
].join(", ");
/** 배관 유출 칸 — 읽기 줄(54-1) · 단 수 줄(폼 「추가 흙막이(단)」 칸이 맡음 · 54-27)은 안 받음. */
const PIPE_OUTLET_ROWS = SIDE_ROWS.replace(".b06-structure-panel__struct--readout, ", "").replace(
".b06-structure-panel__struct--tier, ",
"",
);
/** 배관 칸 = 폼에 자리 없는 집수정 내공 · 유입 단 수만 — 목록에서 골라도 같은 글(54-29).
* 유출 단 수는 폼 「추가 흙막이(단)」 칸(54-27) · 읽기 · 옵션 줄은 안 받음. */
const PIPE_INLET_ROWS = [
".b06-structure-panel__struct--tier",
".b06-structure-panel__struct--basin-size",
].join(", ");
const PIPE_OUTLET_ROWS = ".b06-structure-panel__struct--basin-size";
/** 받을 칸에 맞는 줄 — 배관 유입 · 유출 칸이 아니면 옛 줄 그대로. */
function rowsFor(slot: HTMLElement): string {
if (slot === sideSlots?.outlet) return PIPE_OUTLET_ROWS;
return slot === sideSlots?.inlet ? PIPE_INLET_ROWS : SIDE_ROWS;
}
/** 이식 자리가 지금 화면에 서 있는지 — 그 위 어느 줄이든 숨었으면(다른 시설 명세 폼 등) 옮기지 않는다. */
function slotUsable(target: HTMLElement | undefined): target is HTMLElement {
@@ -174,13 +182,10 @@ function relocateSideRows(panelRoot: HTMLElement): void {
moveRows(panelRoot, EXTRA_ROWS, target);
// 단 수·옵션은 그 단이 딸린 부모 벽의 값이라 부모 칸에 그대로 세운다 — 다단을
// 고를 때 그 두 행이 사라지던 것을 막는다(2026-08-30 사용자).
if (slotUsable(ownerSlot))
moveRows(panelRoot, ownerSlot === sideSlots.outlet ? PIPE_OUTLET_ROWS : SIDE_ROWS, ownerSlot);
if (slotUsable(ownerSlot)) moveRows(panelRoot, rowsFor(ownerSlot), ownerSlot);
} else {
if (!slotUsable(target)) return;
// 배관 유출 칸은 읽기 줄(관 길이 · 좌우 · 상하 안내) · 단 수 줄을 안 받음(54-1 · 54-27)
const rows = target === sideSlots.outlet ? PIPE_OUTLET_ROWS : SIDE_ROWS;
moveRows(panelRoot, rows, target);
moveRows(panelRoot, rowsFor(target), target);
}
// 고른 구조물이 어느 쪽인지 그룹 테두리로 알린다(2026-08-29 사용자 지시 3).
const group = target.closest<HTMLElement>("fieldset");
@@ -188,7 +193,9 @@ function relocateSideRows(panelRoot: HTMLElement): void {
group.classList.add("is-active");
// 이름표에 지금 만지는 구조물을 적는다 — "유입구 (기슭막이)", "유출구 (기슭막이(독립))".
// 추가 기슭막이 칸은 이름 자체가 그 단이라 괄호 안에 단 번호가 온다(2026-08-30 지시 1).
setGroupLabel(group, panelRoot.dataset.panelTitle ?? "");
// 배관 칸은 이름표를 안 바꿈 — 목록에서 고른 글과 같게 · 강조만(54-29).
const pipe = target === sideSlots.inlet || target === sideSlots.outlet;
setGroupLabel(group, pipe ? "" : (panelRoot.dataset.panelTitle ?? ""));
// 고른 칸이 화면 밖일 수 있다 — 그 자리로 스크롤한다(지시 2 · 유입 · 유출도 53-1).
group.scrollIntoView({ block: "nearest" });
}
@@ -595,9 +595,12 @@ export function buildStructurePanel(
// 집수정 유입은 계류측 단이 설 자리가 있을 때만 — 벽이 아니라 구체다.
// 다단 벽도 포함한다 — 행은 부모 칸에 서 있고, 그 값(부모 단 수·옵션)을 그대로 쓴다.
const revetWall = ((key === "inlet" || key === "outlet") && !isBasin) || isExtra;
// 배관 유입 벽 = 다단 없음 — 이미 선 단이 있을 때만(목록에서 고른 글과 같게 · 54-29)
const pipeInlet = key === "inlet" && !isBasin && !deps.hiddenPipe();
extraRow.classList.toggle(
"is-hidden",
!revetWall && !(isBasin && tierChannel === "basin" && (tiers.canAdd || tiers.count > 0)),
(!revetWall || (pipeInlet && tiers.count === 0)) &&
!(isBasin && tierChannel === "basin" && (tiers.canAdd || tiers.count > 0)),
);
currentCount = tiers.count;
countValue.textContent = `${tiers.count}단`;
@@ -496,7 +496,8 @@ export function createB06StructuresPanel(deps: B06StructuresPanelDeps): B06Struc
facility: {
setInletStructure: (value) => section.facility.setInletStructure(value),
onInletStructureChange: (handler) => section.facility.onInletStructureChange(handler),
recount: () => section.facility.recount(),
// 다시 그린 상자에 강조를 다시 얹음(흙막이 상자 · 54-29)
recount: () => (section.facility.recount(), refreshAdjustSlots()),
},
overrideOptions: (chainageM, patch) => {
const hit = pipeFacilities.find(
@@ -587,7 +588,9 @@ export function wireStructureSelection(
const syncInletStructure = (): void => {
const chainageM = panel.currentChainage();
const target = chainageM === null ? undefined : sectionAt(chainageM);
if (target) panel.facility.setInletStructure(stationControls.inletStructure.valueFor(target));
if (!target) return;
panel.facility.setInletStructure(stationControls.inletStructure.valueFor(target));
refreshAdjustSlots(); // 폼을 다시 그림 — 고른 상자 강조를 다시 얹음(54-29)
};
const origRevet = stationControls.revetOffset.select;
@@ -75,4 +75,5 @@ def test_자동_자리_읽기_글이_없음() -> None:
def test_배관_유출_칸은_옛_단_수_줄을_안_받음() -> None:
dock = _src("B06_Section/B06_Section_UI_Adjust_Dock.ts")
assert '".b06-structure-panel__struct--tier, ",\n "",' in dock
# 54-29 — 배관 유출 칸 = 집수정 내공 줄만(단 수 · 읽기 · 옵션 줄 안 받음)
assert 'const PIPE_OUTLET_ROWS = ".b06-structure-panel__struct--basin-size";' in dock
@@ -0,0 +1,46 @@
"""횡단에서 고르기 · 목록에서 고르기 좌측 패널 같은 글 (PLAN 54-29).
짝: `B06_Section/B06_Section_UI_Adjust_Dock.ts`(배관 칸 = 이름표 그대로 · 옵션 · 읽기 · 유출 단 수 줄 안 받음) ·
`B06_Section/B06_Section_UI_Cross_Structure_Panel.ts`(배관 유입 벽 단 수 줄 = 선 단이 있을 때만) ·
`B06_Section/B06_Section_UI_Page_Structures_Panel.ts`(폼을 다시 그린 뒤 강조 다시).
화면 확인은 ORCA(목록 · 횡단 innerText · 칸 수 · 값 같음 · 강조 상자 하나만 다름).
"""
from __future__ import annotations
import os
import re
HERE = os.path.dirname(__file__)
def _src(rel: str) -> str:
with open(os.path.join(HERE, "..", "..", rel), encoding="utf-8") as f:
return f.read()
def test_배관_칸은_옵션_읽기_줄을_안_받음() -> None:
dock = _src("B06_Section/B06_Section_UI_Adjust_Dock.ts")
inlet = re.search(r"const PIPE_INLET_ROWS = \[(.*?)\]", dock, re.S)
assert inlet
assert "struct--options" not in inlet.group(1)
assert "struct--readout" not in inlet.group(1)
assert 'const PIPE_OUTLET_ROWS = ".b06-structure-panel__struct--basin-size";' in dock
def test_배관_칸_이름표는_그대로() -> None:
dock = _src("B06_Section/B06_Section_UI_Adjust_Dock.ts")
assert 'setGroupLabel(group, pipe ? "" : (panelRoot.dataset.panelTitle ?? ""));' in dock
def test_배관_유입_단_수_줄은_선_단이_있을_때만() -> None:
panel = _src("B06_Section/B06_Section_UI_Cross_Structure_Panel.ts")
assert "!isBasin && !deps.hiddenPipe()" in panel
assert "(pipeInlet && tiers.count === 0)" in panel
def test_폼을_다시_그리면_강조_다시() -> None:
page = _src("B06_Section/B06_Section_UI_Page_Structures_Panel.ts")
assert "recount: () => (section.facility.recount(), refreshAdjustSlots())" in page
sync = page[page.index("const syncInletStructure") :]
assert sync.index("refreshAdjustSlots()") < sync.index("};")